EMMA Coverage Report (generated Sun May 02 20:42:29 CEST 2010)
[all classes][hu.netmind.beankeeper.object.impl]

COVERAGE SUMMARY FOR SOURCE FILE [ObjectTrackerImpl.java]

nameclass, %method, %block, %line, %
ObjectTrackerImpl.java100% (4/4)92%  (47/51)83%  (753/909)90%  (184.8/205)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ObjectTrackerImpl100% (1/1)92%  (23/25)81%  (631/775)90%  (152.8/170)
equals (Object, Object): boolean 0%   (0/1)0%   (0/23)0%   (0/3)
makeCurrent (Object, Long): void 0%   (0/1)0%   (0/22)0%   (0/5)
updateCommit (Object, Long): void 100% (1/1)62%  (18/29)86%  (6/7)
updateRollback (Object, Long): void 100% (1/1)68%  (23/34)88%  (7/8)
getObjectData (Object): ObjectTrackerImpl$ObjectData 100% (1/1)72%  (13/18)67%  (2/3)
registerObject (Object, Long, Long, Long, Long): void 100% (1/1)76%  (185/243)91%  (41.7/46)
updateObject (Object, Map): void 100% (1/1)78%  (18/23)95%  (4.8/5)
updateObjectId (Long, Map): void 100% (1/1)83%  (39/47)86%  (10.4/12)
hasChanged (ClassInfo, Object, String, Map, Long): boolean 100% (1/1)99%  (160/161)100% (27/27)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
ObjectTrackerImpl (): void 100% (1/1)100% (12/12)100% (5/5)
access$500 (ObjectTrackerImpl): HashMap 100% (1/1)100% (3/3)100% (1/1)
exists (Object): boolean 100% (1/1)100% (11/11)100% (4/4)
getCurrentAttributes (Object): Map 100% (1/1)100% (8/8)100% (2/2)
getIdentifier (Object): Long 100% (1/1)100% (11/11)100% (4/4)
getMetaData (Object): PersistenceMetaData 100% (1/1)100% (8/8)100% (2/2)
getWrapper (Object): ObjectTracker$ObjectWrapper 100% (1/1)100% (7/7)100% (1/1)
init (Map): void 100% (1/1)100% (22/22)100% (5/5)
makeExist (Object): void 100% (1/1)100% (10/10)100% (4/4)
makeUnexist (Object): void 100% (1/1)100% (13/13)100% (5/5)
notifyValueLeave (Object): void 100% (1/1)100% (21/21)100% (5/5)
registerObject (Object): void 100% (1/1)100% (8/8)100% (2/2)
release (): void 100% (1/1)100% (1/1)100% (1/1)
updateCurrent (Object, Long): void 100% (1/1)100% (15/15)100% (5/5)
updateObject (Object, Long, Long, Long): void 100% (1/1)100% (21/21)100% (8/8)
     
class ObjectTrackerImpl$ObjectWrapperImpl100% (1/1)80%  (4/5)88%  (37/42)82%  (9/11)
getIdentifier (): Long 0%   (0/1)0%   (0/3)0%   (0/1)
equals (Object): boolean 100% (1/1)83%  (10/12)67%  (2/3)
ObjectTrackerImpl$ObjectWrapperImpl (ObjectTrackerImpl, ObjectTracker, Object... 100% (1/1)100% (17/17)100% (5/5)
getObject (): Object 100% (1/1)100% (3/3)100% (1/1)
hashCode (): int 100% (1/1)100% (7/7)100% (1/1)
     
class ObjectTrackerImpl$ObjectData100% (1/1)93%  (13/14)88%  (53/60)94%  (16/17)
compareTo (Object): int 0%   (0/1)0%   (0/7)0%   (0/1)
ObjectTrackerImpl$ObjectData (ObjectTrackerImpl): void 100% (1/1)100% (6/6)100% (2/2)
access$000 (ObjectTrackerImpl$ObjectData): boolean 100% (1/1)100% (3/3)100% (1/1)
access$100 (ObjectTrackerImpl$ObjectData, boolean): void 100% (1/1)100% (4/4)100% (1/1)
access$200 (ObjectTrackerImpl$ObjectData, Long): void 100% (1/1)100% (4/4)100% (1/1)
currentlyExists (): boolean 100% (1/1)100% (3/3)100% (1/1)
exists (): boolean 100% (1/1)100% (3/3)100% (1/1)
getId (): Long 100% (1/1)100% (3/3)100% (1/1)
getMetaData (): PersistenceMetaDataImpl 100% (1/1)100% (3/3)100% (1/1)
getSharedData (): ObjectTrackerImpl$SharedData 100% (1/1)100% (8/8)100% (1/1)
setCurrentlyExists (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setExists (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setId (Long): void 100% (1/1)100% (4/4)100% (2/2)
setMetaData (PersistenceMetaDataImpl): void 100% (1/1)100% (4/4)100% (2/2)
     
class ObjectTrackerImpl$SharedData100% (1/1)100% (7/7)100% (32/32)100% (7/7)
ObjectTrackerImpl$SharedData (ObjectTrackerImpl): void 100% (1/1)100% (9/9)100% (3/3)
access$300 (ObjectTrackerImpl$SharedData): Map 100% (1/1)100% (3/3)100% (1/1)
access$302 (ObjectTrackerImpl$SharedData, Map): Map 100% (1/1)100% (5/5)100% (1/1)
access$400 (ObjectTrackerImpl$SharedData): Map 100% (1/1)100% (3/3)100% (1/1)
access$402 (ObjectTrackerImpl$SharedData, Map): Map 100% (1/1)100% (5/5)100% (1/1)
getRefCounter (): int 100% (1/1)100% (3/3)100% (1/1)
setRefCounter (int): void 100% (1/1)100% (4/4)100% (2/2)

1/**
2 * Copyright (C) 2006 NetMind Consulting Bt.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 
19package hu.netmind.beankeeper.object.impl;
20 
21import hu.netmind.beankeeper.common.StoreException;
22import hu.netmind.beankeeper.type.TypeHandler;
23import hu.netmind.beankeeper.type.TypeHandlerTracker;
24import hu.netmind.beankeeper.model.*;
25import hu.netmind.beankeeper.object.ObjectTracker;
26import hu.netmind.beankeeper.object.PersistenceMetaData;
27import hu.netmind.beankeeper.object.Identifier;
28import hu.netmind.beankeeper.logging.SnapshotLogger;
29import java.util.*;
30import java.lang.ref.WeakReference;
31import org.apache.log4j.Logger;
32 
33/**
34 * This class tracks objects' state for different transactions.
35 * Basically this tracker can answer questions about the state of a
36 * previously registered object.<br>
37 * @author Brautigam Robert
38 * @version Revision: $Revision$
39 */
40public class ObjectTrackerImpl implements ObjectTracker, WeakMapListener
41{
42   private static Logger logger = Logger.getLogger(ObjectTrackerImpl.class);
43   
44   private Random random;
45   private WeakMap objectData;
46   private HashMap sharedData;
47 
48   private ClassTracker classTracker = null; // Injected
49   private TypeHandlerTracker typeHandlerTracker = null; // Injected
50   private SnapshotLogger snapshotLogger = null; // Injected
51 
52   public void init(Map parameters)
53   {
54      random = new Random();
55      sharedData = new HashMap();
56      objectData = new WeakMap(snapshotLogger);
57      objectData.setListener(this);
58   }
59 
60   public void release()
61   {
62   }
63 
64   /**
65    * Get the data structure for an object.
66    */
67   public ObjectData getObjectData(Object obj)
68   {
69      // Only the objectData needs to be synchronized, because only one
70      // transaction can write these objects at a given time. So many
71      // readers may be using the objectdata at a time, but only
72      // one can write it.
73      synchronized ( objectData )
74      {
75         return (ObjectData) objectData.get(obj);
76      }
77   }
78 
79   /**
80    * Return whether a given attribute of a given object changed.
81    */
82   public boolean hasChanged(ClassInfo info, Object obj, String attributeName, Map dbAttributes,
83         Long serial)
84   {
85      // If object does no exists, then all attributes are changed. If
86      // object exists, then the dbAttributes contains the current
87      // attributes, as found in the database.
88      if ( ! exists(obj) )
89         return true;
90      // Get values  
91      Object value = info.getAttributeValue(obj,attributeName);
92      Object dbValue = dbAttributes.get(attributeName);
93      // Check nulls
94      if ( (dbValue==null) && (value==null) )
95      {
96         logger.debug("both values were null, not changed.");
97         return false;
98      }
99      if ( ((dbValue!=null) && (value==null)) ||
100           ((dbValue==null) && (value!=null)) )
101      {
102         logger.debug("one value was null, and the other non-null, so changed.");
103         return true;
104      }
105      // Check handled types
106      Class type = info.getAttributeType(attributeName);
107      if ( typeHandlerTracker.isHandled(type) )
108      {
109         TypeHandler handler = typeHandlerTracker.getHandler(type);
110         boolean result = handler.hasChanged(info,obj,attributeName,dbAttributes,serial); 
111         logger.debug("value was a tracked object, result will be: "+result);
112         return result;
113      }
114      // Check byte array
115      if ( dbValue instanceof byte[] )
116      {
117         boolean result = ! Arrays.equals((byte[]) dbValue, (byte[]) value);
118         logger.debug("values were byte arrays, changed: "+result);
119         return result;
120      }
121      // Check custom objects, in which case the dbValue is a persistence_id,
122      // and the 'value' is an object.
123      if ( classTracker.getType(value.getClass()) == ClassTracker.ClassType.TYPE_OBJECT )
124      {
125         boolean result = ! dbValue.equals(getIdentifier(value));
126         logger.debug("value was a custom object, it's persistence id changed: "+result);
127         return result;
128 
129      }
130      // Fallback to equals()
131      boolean result = !value.equals(dbValue);
132      logger.debug("comparing values with equals(), changed: "+result+" ("+value.getClass().getName()+" vs. db "+dbValue.getClass().getName()+")");
133      return result;
134   }
135 
136   /**
137    * Determines, whether two objects are of the same database instance.
138    * Two objects are the same, if their ids equal. Note however, that
139    * they do not need to contain the same values, or be of same version!
140    */
141   public boolean equals(Object o1, Object o2)
142   {
143      Long id1 = getIdentifier(o1);
144      Long id2 = getIdentifier(o2);
145      return (o1==o2) || ((id1!=null) && (id2!=null) && (id1.equals(id2)));
146   }
147 
148   /**
149    * Determine whether an object exists.
150    * @return True, if object exists in store. This means that searches
151    * will return the object. If this is false, the object cannot be
152    * found yet.
153    */
154   public boolean exists(Object obj)
155   {
156      ObjectData data = getObjectData(obj);
157      if ( data == null )
158         return false;
159      return data.currentlyExists();
160   }
161 
162   /**
163    * Mark object as existent. If this is inside a transaction,
164    * the existence will only be permanent, if object is saved inside
165    * the transaction.
166    */
167   public void makeExist(Object obj)
168   {
169      ObjectData data = getObjectData(obj);
170      if ( data != null )
171         data.setCurrentlyExists(true);
172   }
173 
174   /**
175    * Make the object current at the given serial. If the object is 
176    * more current, nothing is done. It is assumed the object exists.
177    */
178   public void makeCurrent(Object obj, Long serial)
179   {
180      PersistenceMetaDataImpl persistenceMeta = getObjectData(obj).getMetaData();
181      Long lastCurrentSerial = persistenceMeta.getLastCurrentSerial();
182      if ( (serial!=null) && ((lastCurrentSerial==null) || 
183               (lastCurrentSerial.longValue() < serial.longValue() )) )
184         persistenceMeta.setLastCurrentSerial(serial);
185   }
186 
187   /**
188    * Mark an object as non-existent. This happens, if the object gets
189    * deleted, but there are some object instances that are used.
190    */
191   public void makeUnexist(Object obj)
192   {
193      ObjectData data = getObjectData(obj);
194      if ( data != null )
195      {
196         data.setExists(false);
197         data.setCurrentlyExists(false);
198      }
199   }
200 
201   /**
202    * Register a non-existing object into the tracker. If the object is
203    * tracked by this tracker, then nothing is done.
204    * @param obj The object to register.
205    */
206   public void registerObject(Object obj)
207   {
208      registerObject(obj,null,null,null,null);
209   }
210 
211   /**
212    * Register an object into the tracker. This means, the object will
213    * get an id, and associated tracking data structure will be allocated.
214    * If the object given has an attribute named persistenceId, then use
215    * that to re-attach the object to the tracker.
216    * @param obj The object to register.
217    * @param id The id of object. If this is given (not null), the object is 
218    * assumed to exist.
219    * @param serial The current serial, can be null if unknown.
220    * @param startSerial The start of this object, can be null, if unknown.
221    * @param endSerial The ens serial of this object, if it's deleted, can be null.
222    */
223   public void registerObject(Object obj, Long id, Long serial,
224         Long startSerial, Long endSerial)
225   {
226      ObjectData data = getObjectData(obj);
227      if ( data != null )
228         return;
229      // Create object data
230      data = new ObjectData();
231      if ( id == null )
232      {
233         // Check, whether object has 'persistenceId' given
234         ClassInfo info = classTracker.getClassInfo(obj.getClass(),obj);
235         if ( (info.getAttributeNames().contains("persistenceid")) &&
236              (info.getAttributeValue(obj,"persistenceid")!=null) &&
237              (((Long)info.getAttributeValue(obj,"persistenceid")).longValue()!=0) )
238         {
239            Long persistenceId = (Long) info.getAttributeValue(obj,"persistenceid");
240            ClassEntry idEntry = classTracker.getClassEntry(
241                  new Identifier(persistenceId).getClassId());
242            if ( ! info.getSourceEntry().equals(idEntry) )
243               throw new StoreException("trying to re-attach object, but given persistenceid: "+
244                     persistenceId+" indicates entry: "+idEntry+", object is: "+info.getSourceEntry());
245            // Object must be re-attached. Re-attached objects
246            // are assumed to exists. If the user assigns false ids
247            // to objects, this would mean the exists() function would
248            // return false information.
249            data.setId(persistenceId);
250            data.setExists(true);
251            data.setCurrentlyExists(true);
252            if ( logger.isDebugEnabled() )
253               logger.debug("object re-attached as: "+persistenceId);
254         } else {
255            // This is a whole new object, so create new id
256            data.setId(classTracker.getNextId(new ClassEntry(obj)));
257            data.setExists(false);
258            data.setCurrentlyExists(false);
259            if ( logger.isDebugEnabled() )
260               logger.debug("aquired new id to new object: "+data.getId());
261         }
262      } else {
263         data.setId(id);
264         data.setExists(true);
265         data.setCurrentlyExists(true);
266         if ( logger.isDebugEnabled() )
267            logger.debug("object loaded with id: "+data.getId());
268      }
269      PersistenceMetaDataImpl metaData = new PersistenceMetaDataImpl();
270      data.setMetaData(metaData);
271      metaData.setPersistenceId(data.getId());
272      metaData.setPersistenceStart(startSerial);
273      metaData.setPersistenceEnd(endSerial);
274      metaData.setRegistrationSerial(serial);
275      metaData.setLastCurrentSerial(serial);
276      metaData.setObjectClass(obj.getClass());
277      synchronized ( objectData )
278      {
279         objectData.put(obj,data,data.getId());
280         // Create shared state, if not present
281         SharedData sData = data.getSharedData();
282         if ( sData == null )
283            sData = new SharedData();
284         else
285            sData.setRefCounter(sData.getRefCounter()+1);
286         sharedData.put(data.getId(),sData);
287      }
288      // Profile
289      snapshotLogger.log("sharedmap","Shared data size is: "+sharedData.size());
290   }
291 
292   /**
293    * Called when an object leaves the weak map. Note: This is thread-safe
294    * because it is called from WeakMap, which is handled safely in this
295    * class.
296    */
297   public void notifyValueLeave(Object id)
298   {
299      // Object is leaving weakmap, so remove shared state too if nescessary.
300      SharedData data = (SharedData) sharedData.get(id);
301      // Decrease count, remove if no object reference it
302      data.setRefCounter(data.getRefCounter()-1);
303      if ( data.getRefCounter() <= 0 )
304         sharedData.remove(id);
305   }
306 
307   /**
308    * Get the identifier for a given object.
309    * @param obj The object to identify.
310    * @return The identifier, or 0 if the object is not registered.
311    */
312   public Long getIdentifier(Object obj)
313   {
314      ObjectData data = getObjectData(obj);
315      if ( data == null )
316         return null;
317      return data.getId();
318   }
319 
320   /**
321    * Get the meta data associated with the object.
322    */
323   public PersistenceMetaData getMetaData(Object obj)
324   {
325      // Register to be sure
326      registerObject(obj);
327      // Get the metadata
328      return getObjectData(obj).getMetaData();
329   }
330 
331   /**
332    * Get the current attributes of an object.
333    * @return The current attributes of the object, null
334    * if it's not known.
335    */
336   public Map getCurrentAttributes(Object obj)
337   {
338      ObjectData data = getObjectData(obj);
339      return data.getSharedData().attributes;
340   }
341 
342   /**
343    * The object commited, all known changes should be made permanent now.
344    * @param obj The object to update.
345    * @param serial The serial of this update.
346    */
347   public void updateCommit(Object obj, Long serial)
348   {
349      ObjectData data = (ObjectData) getObjectData(obj);
350      if ( data == null )
351      {
352         logger.warn("something may be wrong, an object commited which was not tracked, object was: "+obj);
353      } else {
354         // Set object transaction exists to stable exists
355         data.setExists(data.currentlyExists());
356         // Commit transactional attributes
357         SharedData sData = data.getSharedData();
358         sData.originalAttributes = null;
359      }
360   }
361   
362   /**
363    * Indicate that the object is current at the given serial for sure.
364    * @param obj The object to update.
365    * @param serial The serial on which object was sure to be current.
366    */
367   public void updateCurrent(Object obj, Long serial)
368   {
369      // Update meta-data
370      PersistenceMetaDataImpl metaData = (PersistenceMetaDataImpl) getMetaData(obj);
371      if ( metaData.getPersistenceStart() == null )
372         metaData.setPersistenceStart(serial);
373      metaData.setLastCurrentSerial(serial);
374   }
375   
376   /**
377    * The object rolled back, all known changes should be rolled back too.
378    * @param obj The object to update.
379    * @param serial The serial of this update.
380    */
381   public void updateRollback(Object obj, Long serial)
382   {
383      ObjectData data = (ObjectData) getObjectData(obj);
384      if ( data == null )
385      {
386         logger.warn("something may be wrong, an object rolled back which was not tracked, object was: "+obj);
387      } else {
388         // Set current exists flag to stable exists flag
389         data.setCurrentlyExists(data.exists());
390         // Clear transactional attributes
391         SharedData sData = data.getSharedData();
392         sData.attributes = sData.originalAttributes;
393         sData.originalAttributes = null;
394      }
395   }
396   
397   /**
398    * Update the object's data to indicate object is known to be current
399    * at the given serial. It is assumed that the object exists.
400    * @param obj The object to update.
401    * @param serial The serial of this update.
402    */
403   public void updateObject(Object obj, Long serial, Long startSerial, Long endSerial)
404   {
405      PersistenceMetaDataImpl metaData = getObjectData(obj).getMetaData();
406      if ( serial != null )
407         metaData.setLastCurrentSerial(serial);
408      if ( endSerial != null )
409         metaData.setPersistenceEnd(endSerial);
410      if ( startSerial != null )
411         metaData.setPersistenceStart(startSerial);
412   }
413   
414   /**
415    * Update the current state of the given object.
416    * @param obj The object to update.
417    * @param attributes The attributes which indicate the _current_ state of
418    * the object. If this attribute is null, then this indicates, that
419    * the object has changed, but the current state is not known. 
420    */
421   public void updateObject(Object obj, Map attributes)
422   {
423      synchronized ( objectData )
424      {
425         // Get data
426         ObjectData data = getObjectData(obj);
427         updateObjectId(data.getId(),attributes);
428      }
429   }
430   
431   /**
432    * Update the current state of the given object.
433    * @param id The id of the object to update.
434    * @param attributes The attributes which indicate the _current_ state of
435    * the object. If this attribute is null, then this indicates, that
436    * the object has changed, but the current state is not known. 
437    */
438   public void updateObjectId(Long id, Map attributes)
439   {
440      synchronized ( objectData )
441      {
442         SharedData sData = (SharedData) sharedData.get(id);
443         if ( sData == null )
444            return;
445         // Set state
446         if ( attributes == null )
447         {
448            sData.attributes = null;
449            sData.originalAttributes = null;
450         } else {
451            sData.attributes=attributes;
452            if ( sData.originalAttributes == null )
453               sData.originalAttributes=attributes;
454         }
455      }
456   }
457 
458   /**
459    * Get an object wrapper for an object which disregards object's
460    * own equals() and hashCode() methods.
461    */
462   public ObjectWrapper getWrapper(Object obj)
463   {
464      return new ObjectWrapperImpl(this,obj);
465   }
466 
467   /**
468    * Search for an item in a list based on id.
469    */
470   /**
471    * This is the structure tracked for an object.
472    */
473   public class ObjectData implements Comparable
474   {
475      private Long id;
476      private boolean exists;
477      private boolean currentlyExists; // Inside transaction it may differ from 'exists'.
478      private PersistenceMetaDataImpl metaData;
479 
480      public ObjectData()
481      {
482      }
483 
484      public PersistenceMetaDataImpl getMetaData()
485      {
486         return metaData;
487      }
488      public void setMetaData(PersistenceMetaDataImpl metaData)
489      {
490         this.metaData=metaData;
491      }
492 
493      public int compareTo(Object obj)
494      {
495         return id.compareTo(((ObjectData)obj).id);
496      }
497 
498      public SharedData getSharedData()
499      {
500         return (SharedData) sharedData.get(id);
501      }
502 
503      public Long getId()
504      {
505         return id;
506      }
507      private void setId(Long id)
508      {
509         this.id=id;
510      }
511 
512      public boolean exists()
513      {
514         return exists;
515      }
516      private void setExists(boolean exists)
517      {
518         this.exists=exists;
519      }
520      private boolean currentlyExists()
521      {
522         return currentlyExists;
523      }
524      public void setCurrentlyExists(boolean currentlyExists)
525      {
526         this.currentlyExists=currentlyExists;
527      }
528   }
529 
530   /**
531    * This is a structure which is shared for objects of the same id.
532    */
533   public class SharedData
534   {
535      private Map attributes; // Attributes of _current_ version
536      private Map originalAttributes; // Original attributes before tx
537      private int refCounter;
538 
539      public SharedData()
540      {
541         refCounter=1;
542      }
543 
544      public int getRefCounter()
545      {
546         return refCounter;
547      }
548      public void setRefCounter(int refCounter)
549      {
550         this.refCounter=refCounter;
551      }
552 
553   }
554 
555   public class ObjectWrapperImpl implements ObjectWrapper
556   {
557      private Object obj;
558      private Long id;
559 
560      public ObjectWrapperImpl(ObjectTracker objectTracker, Object obj)
561      {
562         this.obj=obj;
563         registerObject(obj);
564         this.id=objectTracker.getIdentifier(obj);
565      }
566 
567      public Long getIdentifier()
568      {
569         return id;
570      }
571 
572      public Object getObject()
573      {
574         return obj;
575      }
576 
577      public int hashCode()
578      {
579         return (int) (id.longValue()>>32);
580      }
581 
582      public boolean equals(Object rhs)
583      {
584         if ( ! (rhs instanceof ObjectWrapperImpl) )
585            return false;
586         return id.equals(((ObjectWrapperImpl) rhs).id);
587      }
588   }
589}
590 

[all classes][hu.netmind.beankeeper.object.impl]
EMMA 2.0.5312debian (C) Vladimir Roubtsov