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

COVERAGE SUMMARY FOR SOURCE FILE [StoreServiceImpl.java]

nameclass, %method, %block, %line, %
StoreServiceImpl.java100% (5/5)95%  (38/40)81%  (1890/2329)85%  (351.8/413)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class StoreServiceImpl100% (1/1)100% (14/14)77%  (1382/1784)83%  (256.9/309)
remove (Object): void 100% (1/1)66%  (211/320)67%  (34.4/51)
save (Object): void 100% (1/1)78%  (1020/1308)84%  (187.1/222)
getTransactionContent (Transaction): StoreServiceImpl$TransactionContentImpl 100% (1/1)86%  (31/36)92%  (7.4/8)
<static initializer> 100% (1/1)100% (7/7)100% (2/2)
StoreServiceImpl (): void 100% (1/1)100% (50/50)100% (17/17)
access$1000 (StoreServiceImpl): LockTracker 100% (1/1)100% (3/3)100% (1/1)
access$1200 (StoreServiceImpl): Database 100% (1/1)100% (3/3)100% (1/1)
access$1400 (StoreServiceImpl): NodeManager 100% (1/1)100% (3/3)100% (1/1)
access$700 (StoreServiceImpl): ObjectTracker 100% (1/1)100% (3/3)100% (1/1)
access$800 (StoreServiceImpl): EventDispatcher 100% (1/1)100% (3/3)100% (1/1)
access$900 (): Logger 100% (1/1)100% (2/2)100% (1/1)
init (Map): void 100% (1/1)100% (30/30)100% (5/5)
notifyChange (List, Long, Long): void 100% (1/1)100% (11/11)100% (2/2)
release (): void 100% (1/1)100% (5/5)100% (2/2)
     
class StoreServiceImpl$TransactionContentImpl100% (1/1)89%  (17/19)88%  (104/118)83%  (20/24)
setRemoveTables (List): void 0%   (0/1)0%   (0/7)0%   (0/2)
setSaveTables (List): void 0%   (0/1)0%   (0/7)0%   (0/2)
StoreServiceImpl$TransactionContentImpl (): void 100% (1/1)100% (23/23)100% (6/6)
StoreServiceImpl$TransactionContentImpl (StoreServiceImpl$1): void 100% (1/1)100% (3/3)100% (1/1)
access$1100 (StoreServiceImpl$TransactionContentImpl): List 100% (1/1)100% (3/3)100% (1/1)
access$1300 (StoreServiceImpl$TransactionContentImpl): List 100% (1/1)100% (3/3)100% (1/1)
access$300 (StoreServiceImpl$TransactionContentImpl, Object): void 100% (1/1)100% (4/4)100% (1/1)
access$400 (StoreServiceImpl$TransactionContentImpl, String): void 100% (1/1)100% (4/4)100% (1/1)
access$500 (StoreServiceImpl$TransactionContentImpl, String): void 100% (1/1)100% (4/4)100% (1/1)
access$600 (StoreServiceImpl$TransactionContentImpl, Object): void 100% (1/1)100% (4/4)100% (1/1)
addRemoveTable (String): void 100% (1/1)100% (6/6)100% (2/2)
addRemovedObject (Object): void 100% (1/1)100% (6/6)100% (2/2)
addSaveTable (String): void 100% (1/1)100% (6/6)100% (2/2)
addSavedObject (Object): void 100% (1/1)100% (6/6)100% (2/2)
getRemoveTables (): List 100% (1/1)100% (6/6)100% (1/1)
getRemovedObjects (): List 100% (1/1)100% (4/4)100% (1/1)
getSaveTables (): List 100% (1/1)100% (6/6)100% (1/1)
getSavedObjects (): List 100% (1/1)100% (4/4)100% (1/1)
isEmpty (): boolean 100% (1/1)100% (12/12)100% (1/1)
     
class StoreServiceImpl$1100% (1/1)100% (1/1)88%  (23/26)88%  (0.9/1)
<static initializer> 100% (1/1)88%  (23/26)88%  (0.9/1)
     
class StoreServiceImpl$HighTransactionListener100% (1/1)100% (3/3)92%  (116/126)87%  (27/31)
handle (PersistenceEvent): void 100% (1/1)91%  (106/116)87%  (26/30)
StoreServiceImpl$HighTransactionListener (StoreServiceImpl): void 100% (1/1)100% (6/6)100% (1/1)
StoreServiceImpl$HighTransactionListener (StoreServiceImpl, StoreServiceImpl$... 100% (1/1)100% (4/4)100% (1/1)
     
class StoreServiceImpl$LowTransactionListener100% (1/1)100% (3/3)96%  (265/275)98%  (49/50)
handle (PersistenceEvent): void 100% (1/1)96%  (255/265)98%  (48/49)
StoreServiceImpl$LowTransactionListener (StoreServiceImpl): void 100% (1/1)100% (6/6)100% (1/1)
StoreServiceImpl$LowTransactionListener (StoreServiceImpl, StoreServiceImpl$1... 100% (1/1)100% (4/4)100% (1/1)

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.store.impl;
20 
21import org.apache.log4j.Logger;
22import java.util.*;
23import hu.netmind.beankeeper.common.StoreException;
24import hu.netmind.beankeeper.store.StoreService;
25import hu.netmind.beankeeper.serial.SerialTracker;
26import hu.netmind.beankeeper.lock.LockTracker;
27import hu.netmind.beankeeper.config.ConfigurationTracker;
28import hu.netmind.beankeeper.parser.*;
29import hu.netmind.beankeeper.node.*;
30import hu.netmind.beankeeper.event.EventDispatcher;
31import hu.netmind.beankeeper.event.PersistenceEvent;
32import hu.netmind.beankeeper.event.PersistenceEventListener;
33import hu.netmind.beankeeper.model.*;
34import hu.netmind.beankeeper.transaction.Transaction;
35import hu.netmind.beankeeper.transaction.TransactionStatistics;
36import hu.netmind.beankeeper.transaction.TransactionTracker;
37import hu.netmind.beankeeper.db.SearchResult;
38import hu.netmind.beankeeper.db.Database;
39import hu.netmind.beankeeper.object.PersistenceMetaData;
40import hu.netmind.beankeeper.object.ObjectTracker;
41import hu.netmind.beankeeper.object.Identifier;
42import hu.netmind.beankeeper.serial.Serial;
43import hu.netmind.beankeeper.type.TypeHandler;
44import hu.netmind.beankeeper.type.TypeHandlerTracker;
45import hu.netmind.beankeeper.management.ManagementTracker;
46import hu.netmind.beankeeper.node.NodeManager;
47import hu.netmind.beankeeper.query.QueryService;
48import hu.netmind.beankeeper.schema.SchemaManager;
49import hu.netmind.beankeeper.store.event.*;
50import hu.netmind.beankeeper.transaction.event.*;
51import javax.sql.DataSource;
52 
53/**
54 * This store class is the entry point to the persistence library. To
55 * store, remove or select given objects, just use the appropriate
56 * methods.
57 * @author Brautigam Robert
58 * @version Revision: $Revision$
59 */
60public class StoreServiceImpl implements StoreService
61{
62   private static Logger logger = Logger.getLogger(StoreServiceImpl.class);
63   private static Logger operationsLogger = Logger.getLogger("hu.netmind.beankeeper.operations");
64 
65   private StoreStatistics storeStatistics = null;
66   private Map<Transaction,TransactionContentImpl> transactionContent = new HashMap<Transaction,TransactionContentImpl>();
67 
68   private EventDispatcher eventDispatcher = null; // Injected
69   private ManagementTracker managementTracker = null; // Injected
70   private TransactionTracker transactionTracker = null; // Injected
71   private ObjectTracker objectTracker = null; // Injected
72   private NodeManager nodeManager = null; // Injected
73   private SerialTracker serialTracker = null; // Injected
74   private Database database = null; // Injected
75   private ClassTracker classTracker = null; // Injected
76   private LockTracker lockTracker = null; // Injected
77   private TypeHandlerTracker typeHandlerTracker = null; // Injected
78   private ConfigurationTracker config = null; // Injected
79   private QueryService queryService = null; // Injected
80   private SchemaManager schemaManager = null; // Injected
81 
82   /**
83    * Construct this store with the given parameters.
84    */
85   public void init(Map parameters)
86   {
87      // {{{ Init the store
88      // Register as event listener
89      eventDispatcher.registerListener(new HighTransactionListener(),
90            EventDispatcher.PRI_SYSTEM_HIGH);
91      eventDispatcher.registerListener(new LowTransactionListener(),
92            EventDispatcher.PRI_SYSTEM_LOW);
93      // Create the operations mbean and register
94      storeStatistics = new StoreStatistics();
95      managementTracker.registerBean("StoreStatistics",storeStatistics);      
96      // }}}
97   }
98 
99   /**
100    * Release resources.
101    */
102   public void release()
103   {
104      // {{{
105      managementTracker.deregisterBean("StoreStatistics");      
106      // }}}
107   }
108 
109   /**
110    * Get or allocate the transaction content.
111    * @return A transaction content for the transaction given.
112    */
113   public TransactionContentImpl getTransactionContent(Transaction transaction)
114   {
115      TransactionContentImpl content = null;
116      synchronized ( transactionContent )
117      {
118         content = transactionContent.get(transaction);
119         if ( content == null )
120         {
121            content = new TransactionContentImpl();
122            transactionContent.put(transaction,content);
123         }
124      }
125      return content;
126   }
127 
128   /**
129    * Save the given object to the store. 
130    * @param obj The object to save.
131    * @throws StoreException If save is not successfull.
132    */
133   public void save(Object obj)
134   {
135      // {{{ Save
136      // Transaction
137      Transaction transaction = transactionTracker.getTransaction(TransactionTracker.TX_REQUIRED);
138      transaction.begin();
139      TransactionStatistics startStats = new TransactionStatistics();
140      startStats.add(transaction.getStats());
141      long startTime = System.currentTimeMillis();
142      try
143      {
144         // Get transaction content
145         TransactionContentImpl content = getTransactionContent(transaction);
146         // Get serials
147         Long currentSerial = serialTracker.getNextSerial();
148         Long currentTxSerial = transaction.getSerial();
149         logger.debug("save called, using serial: "+currentSerial+", tx serial: "+currentTxSerial);
150         // Save object or insert, possibly recursively.
151         // The waitingObjects list is an ordered list which
152         // contains all objects currently waiting for saving.
153         // First it contains the object to be saved, then if
154         // it referenced more objects, those will be appended to the
155         // list. An object can only be saved, when all referencing objects
156         // are all at least created.
157         LinkedList events = new LinkedList();
158         LinkedList savedHandledObjects = new LinkedList();
159         HashSet waitingObjects = new HashSet();
160         waitingObjects.add(objectTracker.getWrapper(obj));
161         while ( waitingObjects.size() > 0 )
162         {
163            if ( logger.isDebugEnabled() )
164               logger.debug("saving object, waiting list: "+waitingObjects.size()+", memory: "+Runtime.getRuntime().freeMemory());
165            // {{{ Selecting objects which wait for saving
166            // Get the last object (the bottom of dependency tree)
167            ObjectTracker.ObjectWrapper currentWrapper = (ObjectTracker.ObjectWrapper) waitingObjects.iterator().next();
168            Object current = currentWrapper.getObject();
169            // Now get object's class info. This method does not
170            // return null. If the class info is not available, it
171            // creates it, if it is available it checks whether to
172            // update the info in database (new attibutes, etc)
173            ClassInfo classInfo = classTracker.getClassInfo(current.getClass(),current);
174            logger.debug("object class info is: "+classInfo);
175            // Makeing sure schema is up to date
176            schemaManager.ensureSchema(classInfo.getSourceEntry());
177            // If object does not exists, register it with object
178            // tracker. Note, that object tracker only leases an id
179            // and exists() will return false until object commited.
180            // Also, if object already has an id, this will do nothing.
181            objectTracker.registerObject(current);
182            Long currentId = objectTracker.getIdentifier(current);
183            logger.debug("saving object with id: "+currentId);
184            // Lock object
185            lockTracker.lock(current);
186            content.addSavedObject(currentWrapper);
187            // Assemble changed attributes of this object into a Map.
188            // If an attribute is not a primitive type it is checked,
189            // if it exists. If it does not, it is appended to waitingObjects
190            // and we start from beginning with this object. If it exists,
191            // then the reference id will be inserted into the change Map.
192            Map changedAttributes = new HashMap();
193            // }}}
194            // {{{ Assemble previous state of object
195            Map originalAttributes = objectTracker.getCurrentAttributes(current);
196            TimeControl originalTimeControl = new TimeControl(currentSerial,currentTxSerial,true);
197            if ( (objectTracker.exists(current)) && (originalAttributes==null) )
198            {
199               // First, get all original attributes from 
200               // the database, because we'll have to create a full row, 
201               // even when a single attribute changed.
202               ClassEntry selectClassEntry = classInfo.getSourceEntry();
203               SpecifiedTableTerm selectTerm = new SpecifiedTableTerm(schemaManager.getTableName(selectClassEntry),null);
204               List relatedClassEntries = classTracker.getRelatedClassEntries(selectClassEntry);
205               for ( int u=0; u<relatedClassEntries.size(); u++ )
206               {
207                  ClassEntry relatedClassEntry = (ClassEntry) relatedClassEntries.get(u);
208                  ClassInfo relatedClassInfo = classTracker.getClassInfo(relatedClassEntry);
209                  if ( relatedClassInfo == null )
210                     throw new ParserException(ParserException.ABORT,"object class not found for loading: '"+relatedClassEntry+"'");
211                  // Create the term and add to mainterm
212                  TableTerm leftTerm = new TableTerm(schemaManager.getTableName(relatedClassEntry),null);
213                  SpecifiedTableTerm.LeftjoinEntry joinEntry = new SpecifiedTableTerm.LeftjoinEntry();
214                  Expression joinExpr = new Expression();
215                  joinExpr.add(new ReferenceTerm(selectTerm,"persistence_id"));
216                  joinExpr.add("=");
217                  joinExpr.add(new ReferenceTerm(leftTerm,"persistence_id"));
218                  joinExpr.add("and");
219                  originalTimeControl.apply(joinExpr,leftTerm);
220                  joinEntry.expression=joinExpr;
221                  joinEntry.term=leftTerm;
222                  selectTerm.getRelatedLeftTerms().add(joinEntry);
223               }
224               Expression expr = new Expression();
225               expr.add(new ReferenceTerm(selectTerm,"persistence_id"));
226               expr.add("=");
227               expr.add(new ConstantTerm(currentId));
228               expr.add("and");
229               originalTimeControl.apply(expr,selectTerm);
230               QueryStatement referredStatement = new QueryStatement(selectTerm,expr,null);
231               referredStatement.setStaticRepresentation("FIND "+selectClassEntry+" where persistence_id = "+currentId);
232               referredStatement.setTimeControl(originalTimeControl);
233               SearchResult referredResult = queryService.find(referredStatement,null);
234               if ( referredResult.getResultSize() > 1 )
235               {
236                  throw new StoreException("object's last state was ambigous, results for object id '"+currentId+"' was: "+referredResult.getResult());
237               } else if ( referredResult.getResultSize() == 1 ) {
238                  // Set original attributes from this last state of object
239                  originalAttributes = (Map) referredResult.getResult().get(0);
240                  if ( logger.isDebugEnabled() )
241                     logger.debug("object original attributes: "+originalAttributes);
242               } else {
243                  // Object was not found, it most likely was deleted
244                  // so mark object as non-existent. This is a trick to
245                  // again save all attributes to database. For example,
246                  // when selecting an old instance, it is possible, the
247                  // instance is deleted in present time, so we must save
248                  // all attributes.
249                  objectTracker.makeUnexist(current);
250               }
251            } else {
252               if ( logger.isDebugEnabled() )
253                  logger.debug("object did not exist, or original attributes known: "+originalAttributes);
254            }
255            // Set attribute maps correctly:
256            // - nonchangedAttributes will be modified to contain only modified attrs
257            // - originalAttributes won't be modified
258            // - newAttributes will be the current state
259            Map nonchangedAttributes = new HashMap();
260            Map newAttributes = new HashMap();
261            if ( originalAttributes != null )
262            {
263               nonchangedAttributes = new HashMap(originalAttributes);
264               newAttributes = new HashMap(originalAttributes);
265            } else {
266               originalAttributes = new HashMap();
267            }
268            // }}}
269            // {{{ Assembling changed attributes
270            List attributeNames = classInfo.getAttributeNames();
271            if ( logger.isDebugEnabled() )
272               logger.debug("class tracker reported object to save has following attributes: "+
273                     attributeNames+", it has id: "+currentId+", object tracker says it exists: "+objectTracker.exists(current));
274            for ( int i=0; i<attributeNames.size(); i++ )
275            {
276               String attributeName = (String) attributeNames.get(i);
277               String attributeNameLowerCase = attributeName.toLowerCase();
278               // Do not handle special attributes
279               if ( ("persistence_id".equals(attributeNameLowerCase)) || 
280                     ("persistenceid".equals(attributeNameLowerCase)) )
281               {
282                  // We found a persistence id, fill it with id
283                  classInfo.setAttributeValue(current,attributeName,currentId);
284                  continue;
285               }
286               // Skip reserved prefix'd attributes
287               if ( attributeNameLowerCase.startsWith("persistence") )
288                  continue;
289               // Handle non-special attributes
290               Object attributeValue = classInfo.getAttributeValue(current,attributeName);
291               logger.debug("saving attribute: "+attributeName+", if changed.");
292               if ( objectTracker.hasChanged(classInfo,current,attributeName,nonchangedAttributes,currentSerial) )
293               {
294                  // Current object's current attribute has changed,
295                  // so arrange it's save.
296                  // This is a switch for the type, this should be
297                  // replaced with something more adequate. For
298                  // example a type manager who will handle types
299                  // and is extensible.
300                  Class attributeType = classInfo.getAttributeType(attributeName);
301                  switch ( classTracker.getType(attributeType) )
302                  {
303                     case TYPE_PRIMITIVE:
304                        logger.debug("changing primitive attribute: "+attributeName+", value: "+current);
305                        // Primitive type, just add to attributes
306                        changedAttributes.put(attributeName,attributeValue);
307                        break;
308                     case TYPE_HANDLED:
309                        logger.debug("changing handled attribute: "+attributeName+", type: "+attributeType);
310                        TypeHandler handler = typeHandlerTracker.getHandler(attributeType);
311                        HashSet saveTables = new HashSet();
312                        HashSet removeTables = new HashSet();
313                        // Call handler save
314                        Object newValue = handler.save(classInfo,current,attributeName,
315                              transaction,currentSerial,
316                              attributeValue,waitingObjects,saveTables,removeTables,events,
317                              changedAttributes,nonchangedAttributes);
318                        // Save the handled objects for later use, they need to
319                        // be notified when all objects are saved
320                        if ( newValue != null )
321                        {
322                           savedHandledObjects.add(handler);
323                           savedHandledObjects.add(newValue);
324                        }
325                        // Add it's saved and remove tables to the transaction content
326                        for ( Object table : saveTables )
327                           content.addSaveTable((String) table);
328                        for ( Object table : removeTables )
329                           content.addRemoveTable((String) table);
330                        break;
331                     case TYPE_OBJECT:
332                        logger.debug("attribute decided as custom object '"+attributeName+
333                              "', type is: "+classInfo.getAttributeType(attributeName));
334                        // Object type
335                        if ( attributeValue == null )
336                        {
337                           // Object type, but null
338                           changedAttributes.put(attributeName,null);
339                        } else {
340                           if ( ! objectTracker.exists(attributeValue) )
341                           {
342                              // Object referred does not exists, so it will
343                              // have to be created after we're done
344                              waitingObjects.add(objectTracker.getWrapper(attributeValue));
345                           }
346                           objectTracker.registerObject(attributeValue);
347                           Long objectId = objectTracker.getIdentifier(attributeValue);
348                           // Got id, so give it to the man
349                           changedAttributes.put(attributeName,objectId);
350                        }
351                        break;
352                     default:
353                        throw new StoreException("unknown type in attribute: "+attributeName+", value was: "+attributeValue);
354                  }
355               }
356            } // End of iterating over attributes
357            newAttributes.putAll(changedAttributes);
358            // }}}
359            // {{{ Saving the object's changed attributes
360            // Now do the database thing on the assembled changed 
361            // attributes, since the waitingObjects list did not
362            // change, meaning no new dependencies were discovered.
363            // All changes are saved in multiple save/inserts according
364            // to class structure.
365            if ( logger.isDebugEnabled() )
366               logger.debug("object (of entry: "+classInfo+") will be saved into entries: "+classInfo.getClassEntries());
367            Iterator strictClassEntriesIterator = classInfo.getClassEntries().iterator();
368            while ( strictClassEntriesIterator.hasNext() )
369            {
370               // Assemble changes for this strict class
371               ClassEntry entry = (ClassEntry) strictClassEntriesIterator.next();
372               HashMap strictChanges = new HashMap();
373               HashMap strictNonChanges = new HashMap();
374               List strictAttributeNames = classInfo.getAttributeNames(entry);
375               if ( logger.isDebugEnabled() )
376                  logger.debug("assembling changed for class: "+entry+", strict attributes: "+strictAttributeNames);
377               for ( int i=0; i<strictAttributeNames.size() ; i++ )
378               {
379                  String attributeName = (String) strictAttributeNames.get(i);
380                  Class attributeType = classInfo.getAttributeType(attributeName);
381                  TypeHandler handler = typeHandlerTracker.getHandler(attributeType);
382                  if ( handler == null )
383                  {
384                     // No handler, this is a simple attribute
385                     if ( changedAttributes.containsKey(attributeName) )
386                        strictChanges.put(attributeName,changedAttributes.remove(attributeName));
387                     if ( nonchangedAttributes.containsKey(attributeName) )
388                        strictNonChanges.put(attributeName,nonchangedAttributes.remove(attributeName));
389                  } else {
390                     // Got handler, then iterate on it's attributes
391                     Map embeddedAttributes = handler.getAttributeTypes(attributeName);
392                     if ( logger.isDebugEnabled() )
393                        logger.debug("attribute: "+attributeName+", was an embedded attribute, adding: "+embeddedAttributes);
394                     Iterator embeddedAttributeNamesIterator = embeddedAttributes.keySet().iterator();
395                     while ( embeddedAttributeNamesIterator.hasNext() )
396                     {
397                        attributeName = (String) embeddedAttributeNamesIterator.next();
398                        if ( changedAttributes.containsKey(attributeName) )
399                           strictChanges.put(attributeName,changedAttributes.remove(attributeName));
400                        if ( nonchangedAttributes.containsKey(attributeName) )
401                           strictNonChanges.put(attributeName,nonchangedAttributes.remove(attributeName));
402                     }
403                  }
404               }
405               // Make changes
406               if ( objectTracker.exists(current) )
407               {
408                  // Save
409                  logger.debug("changing object with following attributes: "+strictChanges+", not changed: "+strictNonChanges);
410                  if ( strictChanges.size() > 0 )
411                  {
412                     // First set enddate on used entry
413                     HashMap removeChanges = new HashMap();
414                     HashMap keys = new HashMap();
415                     keys.put("persistence_id",currentId);
416                     keys.put("persistence_end",Serial.getMaxSerial().getValue());
417                     keys.put("persistence_txend",Serial.getMaxSerial().getValue());
418                     removeChanges.put("persistence_txend",currentSerial);
419                     removeChanges.put("persistence_txendid",currentTxSerial);
420                     database.save(transaction,schemaManager.getTableName(entry),
421                           keys, removeChanges);
422                     content.addRemoveTable(schemaManager.getTableName(entry));
423                     // Create new entry
424                     strictNonChanges.putAll(strictChanges);
425                     strictNonChanges.put("persistence_id", currentId);
426                     strictNonChanges.put("persistence_start", Serial.getMaxSerial().getValue());
427                     strictNonChanges.put("persistence_end", Serial.getMaxSerial().getValue());
428                     strictNonChanges.put("persistence_txendid",new Long(0));
429                     strictNonChanges.put("persistence_txstart", currentSerial);
430                     strictNonChanges.put("persistence_txstartid", currentTxSerial);
431                     strictNonChanges.put("persistence_txend", Serial.getMaxSerial().getValue());
432                     database.insert(transaction,schemaManager.getTableName(entry),
433                           strictNonChanges);
434                     // Add to modified tables list
435                     content.addSaveTable(schemaManager.getTableName(entry));
436                  }
437               } else {
438                  // Insert
439                  logger.debug("inserting object with following attributes: "+strictChanges);
440                  strictChanges.put("persistence_id", currentId);
441                  strictChanges.put("persistence_start", Serial.getMaxSerial().getValue());
442                  strictChanges.put("persistence_end", Serial.getMaxSerial().getValue());
443                  strictChanges.put("persistence_txendid",new Long(0));
444                  strictChanges.put("persistence_txstart", currentSerial);
445                  strictChanges.put("persistence_txstartid", currentTxSerial);
446                  strictChanges.put("persistence_txend", Serial.getMaxSerial().getValue());
447                  database.insert(transaction,schemaManager.getTableName(entry),
448                        strictChanges);
449                  // Add to modified tables list
450                  content.addSaveTable(schemaManager.getTableName(entry));
451               }
452            }
453            if ( changedAttributes.size() != 0 )
454               logger.warn("there are attributes that do not belong in any superclass of object to be saved, classinfo: "+
455                     classInfo+", attributes: "+changedAttributes);
456            logger.debug("saving object with id: "+currentId+" updating meta-data.");
457            // Remove object from waiting list
458            waitingObjects.remove(currentWrapper);
459            // Notify event listeners
460            if ( objectTracker.exists(current) )
461            {
462               // Send modify event. For this, we have to re-create
463               // the original state of the object.
464               events.add(new ModifyObjectEvent(classTracker,objectTracker,typeHandlerTracker,
465                        queryService,originalAttributes,originalTimeControl,current));
466            } else {
467               // Create
468               events.add(new CreateObjectEvent(current));
469            }
470            // Update object tracker
471            objectTracker.makeExist(current); // Exists for this transaction
472            objectTracker.updateObject(current,newAttributes);
473            // }}}
474            logger.debug("saving object with id: "+currentId+" finished.");
475         }
476         // {{{ Go through handled objects, and notify that save ended
477         for ( int i=0; i<savedHandledObjects.size(); )
478         {
479            TypeHandler handler = (TypeHandler) savedHandledObjects.get(i++);
480            Object value = (Object) savedHandledObjects.get(i++);
481            handler.postSave(value);
482         }
483         // }}}
484         // Debug code
485         if ( operationsLogger.isDebugEnabled() )
486         {
487            TransactionStatistics stats = new TransactionStatistics();
488            stats.add(transaction.getStats());
489            stats.substract(startStats);
490            operationsLogger.debug("operation save: "+obj+", "+stats);
491            if ( operationsLogger.isTraceEnabled() )
492               operationsLogger.trace("previous operation trace:",new Exception("trace"));
493         }
494         // {{{ Notify dispatcher of events occured during save
495         for ( int i=0; i<events.size(); i++ )
496            eventDispatcher.notify((PersistenceEvent) events.get(i));
497         // }}}
498         // "Happy, happy, joy, joy". Object saved.
499      } catch ( StoreException e ) {
500         transaction.markRollbackOnly();
501         logger.error("throwing store exception",e);
502         throw e;
503      } catch ( Throwable e ) {
504         transaction.markRollbackOnly();
505         logger.error("throwing unexpected exception",e);
506         throw new StoreException("unexpected exception",e);
507      } finally {
508         transaction.commit();
509         // Add to statistics
510         long endTime = System.currentTimeMillis();
511         synchronized ( storeStatistics )
512         {
513            storeStatistics.setSaveCount(storeStatistics.getSaveCount()+1);
514            storeStatistics.setSaveTime(storeStatistics.getSaveTime()+(endTime-startTime));
515         }
516      }
517      /// }}}
518   }
519 
520   /**
521    * Remove the object given. If the object is not stored yet, no
522    * operation will take place.
523    * @param obj The object to remove.
524    * @throws StoreException If remove is not successfull.
525    */
526   public void remove(Object obj)
527   {
528      // {{{ Remove object
529      // Transaction
530      Transaction transaction = transactionTracker.getTransaction(TransactionTracker.TX_REQUIRED);
531      transaction.begin();
532      TransactionStatistics startStats = new TransactionStatistics();
533      startStats.add(transaction.getStats());
534      long startTime = System.currentTimeMillis();
535      try
536      {
537         // Get transaction content
538         TransactionContentImpl content = getTransactionContent(transaction);
539         // Get id, and at same time register into object tracker,
540         // later attempt remove only if object really exists
541         Long id = objectTracker.getMetaData(obj).getPersistenceId();
542         if ( ! objectTracker.exists(obj) )
543         {
544            logger.debug("object does not exists, so remove will not be attempted");
545            return;
546         }
547         Long currentSerial = serialTracker.getNextSerial();
548         Long currentTxSerial = transaction.getSerial();
549         // Lock object
550         lockTracker.lock(obj);
551         content.addRemovedObject(objectTracker.getWrapper(obj));
552         // Get class info
553         ClassInfo classInfo = classTracker.getClassInfo(obj.getClass(),obj);
554         // Assemble attributes
555         Map removeChanges = new HashMap();
556         logger.debug("remove called, using serial: "+currentSerial);
557         removeChanges.put("persistence_txend", currentSerial);
558         removeChanges.put("persistence_txendid", currentTxSerial);
559         Map keys = new HashMap();
560         keys.put("persistence_id",id);
561         keys.put("persistence_end",Serial.getMaxSerial().getValue());
562         // Execute remove on class and all superclasses, et voila'
563         for ( int i=0; i<classInfo.getClassEntries().size(); i++ )
564         {
565            ClassEntry entry = (ClassEntry) classInfo.getClassEntries().get(i);
566            database.save(transaction,schemaManager.getTableName(entry),keys , removeChanges);
567            // Add changed table
568            content.addRemoveTable(schemaManager.getTableName(entry));
569         }
570         // Notify object tracker
571         objectTracker.makeUnexist(obj);
572         objectTracker.updateObject(obj,null); // Update to null cached attributes
573         // Notify event listeners
574         eventDispatcher.notify(new DeleteObjectEvent(obj));
575         // Debug code
576         if ( operationsLogger.isDebugEnabled() )
577         {
578            TransactionStatistics stats = new TransactionStatistics();
579            stats.add(transaction.getStats());
580            stats.substract(startStats);
581            operationsLogger.debug("operation remove: "+obj+" (id: "+id+"), "+stats);
582            if ( operationsLogger.isTraceEnabled() )
583               operationsLogger.trace("previous operation trace:",new Exception("trace"));
584         }
585      } catch ( StoreException e ) {
586         transaction.markRollbackOnly();
587         logger.error("throwing store exception",e);
588         throw e;
589      } catch ( Throwable e ) {
590         transaction.markRollbackOnly();
591         logger.error("throwing unexpected exception",e);
592         throw new StoreException("unexpected exception",e);
593      } finally {
594         transaction.commit();
595         // Add to statistics
596         long endTime = System.currentTimeMillis();
597         synchronized ( storeStatistics )
598         {
599            storeStatistics.setRemoveCount(storeStatistics.getRemoveCount()+1);
600            storeStatistics.setRemoveTime(storeStatistics.getRemoveTime()+(endTime-startTime));
601         }
602      }
603      /// }}}
604   }
605 
606   // {{{ Helper classes
607   /**
608    * This listener finishes a transaction by unlocking all objects.
609    */
610   private class HighTransactionListener implements PersistenceEventListener
611   {
612      public void handle(PersistenceEvent event)
613      {
614         if ( ! (event instanceof TransactionEvent) )
615            return; // Quick exit
616         Transaction transaction = ((TransactionEvent) event).getTransaction();
617         if ( (event instanceof TransactionRolledbackEvent) ||
618           (event instanceof TransactionCommittedEvent) )
619         {
620            // Remove transaction contents.
621            TransactionContentImpl content = getTransactionContent(transaction);
622            if ( content.isEmpty() )
623               return; // No need to do anything, if there were no modifications
624            // Unlock objects. Unlock event must come after everything about a
625            // transaction is complete, because as soon as objects are unlocked,
626            // other threads or nodes might cause interference with commit or
627            // committed listeners.
628            // If this event does not reach the server, the communication
629            // error will cause the server to unlock all objects anyway.
630            // TODO: do NOT send update event to object tracker, let it listen
631            // for events.
632            ArrayList objects = new ArrayList();
633            Iterator removedObjectsIterator = content.getRemovedObjects().iterator();
634            while ( removedObjectsIterator.hasNext() )
635            {
636               Object obj = ((ObjectTracker.ObjectWrapper)removedObjectsIterator.next()).getObject();
637               objectTracker.updateObject(
638                     obj,transaction.getEndSerial(),null,transaction.getEndSerial());
639               objects.add(obj);
640               try
641               {
642                  eventDispatcher.notify(new FinishedObjectEvent(obj));
643               } catch ( Exception e ) {
644                  logger.warn("exception while notifying finished object",e);
645               }
646            }
647            Iterator savedObjectsIterator = content.getSavedObjects().iterator();
648            while ( savedObjectsIterator.hasNext() )
649            {
650               Object obj = ((ObjectTracker.ObjectWrapper)savedObjectsIterator.next()).getObject();
651               objectTracker.updateObject(
652                     obj,transaction.getEndSerial(),transaction.getEndSerial(),null);
653               objects.add(obj);
654               try
655               {
656                  eventDispatcher.notify(new FinishedObjectEvent(obj));
657               } catch ( Exception e ) {
658                  logger.warn("exception while notifying finished object",e);
659               }
660            }
661            lockTracker.unlock(objects.toArray());
662         }
663      }
664   }
665 
666   /**
667    * This listener is here for finishing a committed transactions by
668    * finalizing changes to the ens serial, and modifying their metadata when successful.
669    */
670   private class LowTransactionListener implements PersistenceEventListener
671   {
672      public void handle(PersistenceEvent event)
673         throws Exception
674      {
675         if ( ! (event instanceof TransactionEvent) )
676            return; // Quick exit
677         Transaction transaction = ((TransactionEvent) event).getTransaction();
678         // Get transaction contents.
679         TransactionContentImpl content = getTransactionContent(transaction);
680         if ( content.isEmpty() )
681            return; // No need to do anything, if there were no modifications
682         List objects = new ArrayList();
683         objects.addAll(content.getRemovedObjects());
684         objects.addAll(content.getSavedObjects());
685         Long serial = transaction.getEndSerial();
686         // Commit object updates
687         if ( event instanceof TransactionCommittedEvent )
688         {
689            // We must update objects to reflect the changes we made
690            Iterator objectsIterator = objects.iterator();
691            while ( objectsIterator.hasNext() )
692            {
693               Object obj = ((ObjectTracker.ObjectWrapper)objectsIterator.next()).getObject();
694               objectTracker.updateCommit(obj,serial);
695            }
696         }
697         if ( event instanceof TransactionRolledbackEvent )
698         {
699            // We must update objects to reflect the changes we made
700            Iterator objectsIterator = objects.iterator();
701            while ( objectsIterator.hasNext() )
702            {
703               Object obj = ((ObjectTracker.ObjectWrapper)objectsIterator.next()).getObject();
704               objectTracker.updateRollback(obj,serial);
705            }
706         }
707         // Make all changes permanent and visible here
708         if ( event instanceof TransactionCommitEndingEvent )
709         {
710            // All operations done must be finalized now. If an exception occurs,
711            // the transaction tracker will roll back the transaction anyway,
712            // so we don't have to worry about that.
713            // Save: set startdates where startdate is maxdate
714            List saveTables = content.getSaveTables();
715            HashMap keys = new HashMap();
716            keys.put("persistence_txstartid",transaction.getSerial());
717            HashMap changes = new HashMap();
718            changes.put("persistence_start",transaction.getEndSerial());
719            for ( int i=0; i<saveTables.size(); i++ )
720            {
721               String tableName = (String) saveTables.get(i);
722               logger.debug("fixing save table: "+tableName);
723               database.save(transaction,tableName,keys,changes);
724            }
725            // Remove: set enddates where txenddate is not maxdate
726            List removeTables = content.getRemoveTables();
727            keys = new HashMap();
728            keys.put("persistence_txendid",transaction.getSerial());
729            changes = new HashMap();
730            changes.put("persistence_end",transaction.getEndSerial());
731            for ( int i=0; i<removeTables.size(); i++ )
732            {
733               String tableName = (String) removeTables.get(i);
734               logger.debug("fixing remove table: "+tableName);
735               database.save(transaction,tableName,keys,changes);
736            }
737            // Notify the server of all objects that changed. This operation
738            // must be before the commit physically occurs, because this notification
739            // will cause the server to know which objects are modified.
740            List metas = new ArrayList();
741            for ( int i=0; i<objects.size(); i++ )
742               metas.add(objectTracker.getMetaData(((ObjectTracker.ObjectWrapper)objects.get(i)).getObject()));
743            if ( logger.isDebugEnabled() )
744               logger.debug("sending changed objects' meta to all: "+metas);
745            // TODO (DODGY): We should ensure that this call does not return until
746            // all nodes finished processing of event. If we don't, it's possible that
747            // a client does not clear cache for example but after the commit lock is
748            // freed. Which would mean it might allow for an inconsistent query.
749            nodeManager.callAll(StoreService.class.getName(),"notifyChange",
750                  new Class[] { List.class, Long.class, Long.class },
751                  new Object[] { metas, transaction.getEndSerial(), transaction.getSerial() });
752            logger.debug("sending changed objects finished.");
753         }
754      }
755   }
756 
757   public void notifyChange(List<PersistenceMetaData> metas, Long serial, Long txSerial)
758   {
759      eventDispatcher.notifyAll(new ObjectsFinalizationEvent(metas,serial,txSerial));
760   }
761 
762   public static class TransactionContentImpl
763   {
764      // Store's attributes
765      private LinkedList savedObjectList;
766      private LinkedList removedObjectList;
767      private HashSet saveTables;
768      private HashSet removeTables;
769 
770      private TransactionContentImpl()
771      {
772         savedObjectList = new LinkedList();
773         removedObjectList = new LinkedList();
774         saveTables = new HashSet();
775         removeTables = new HashSet();
776      }
777 
778      private void addSavedObject(Object obj)
779      {
780         savedObjectList.add(obj);
781      }
782 
783      /**
784       * Get the objects saved in this transaction as a list.
785       */
786      public List getSavedObjects()
787      {
788         return Collections.unmodifiableList(savedObjectList);
789      }
790 
791      private void addRemovedObject(Object obj)
792      {
793         removedObjectList.add(obj);
794      }
795 
796      /**
797       * Get the objects removed in this transaction as a list.
798       */
799      public List getRemovedObjects()
800      {
801         return Collections.unmodifiableList(removedObjectList);
802      }
803 
804      public boolean isEmpty()
805      {
806         return savedObjectList.isEmpty() && removedObjectList.isEmpty();
807      }
808 
809      private void addSaveTable(String table)
810      {
811         saveTables.add(table);
812      }
813      private void setSaveTables(List saveTables)
814      {
815         this.saveTables = new HashSet(saveTables);
816      }
817      private List getSaveTables()
818      {
819         return new ArrayList(saveTables);
820      }
821      private void addRemoveTable(String table)
822      {
823         removeTables.add(table);
824      }
825      private void setRemoveTables(List removeTables)
826      {
827         this.removeTables = new HashSet(removeTables);
828      }
829      private List getRemoveTables()
830      {
831         return new ArrayList(removeTables);
832      }
833 
834   }
835   // }}}
836}
837 
838 
839 

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