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

COVERAGE SUMMARY FOR SOURCE FILE [LockTrackerImpl.java]

nameclass, %method, %block, %line, %
LockTrackerImpl.java100% (2/2)56%  (23/41)80%  (1399/1750)85%  (289.3/342)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class LockTrackerImpl100% (1/1)55%  (22/40)80%  (1393/1744)85%  (288.3/341)
getProvider (): SessionInfoProvider 0%   (0/1)0%   (0/3)0%   (0/1)
lock (Object []): void 0%   (0/1)0%   (0/8)0%   (0/2)
lock (Object [], int): void 0%   (0/1)0%   (0/8)0%   (0/2)
lock (Object, SessionInfo): void 0%   (0/1)0%   (0/13)0%   (0/2)
lockEnsureCurrent (Object []): void 0%   (0/1)0%   (0/8)0%   (0/2)
lockEnsureCurrent (Object [], int): void 0%   (0/1)0%   (0/8)0%   (0/2)
lockEnsureCurrent (Object, SessionInfo): void 0%   (0/1)0%   (0/13)0%   (0/2)
lockEnsureCurrent (Object, int): void 0%   (0/1)0%   (0/13)0%   (0/2)
lockReadOnly (Object []): void 0%   (0/1)0%   (0/8)0%   (0/2)
lockReadOnly (Object [], int): void 0%   (0/1)0%   (0/8)0%   (0/2)
lockReadOnly (Object, SessionInfo): void 0%   (0/1)0%   (0/13)0%   (0/2)
lockReadOnly (Object, int): void 0%   (0/1)0%   (0/13)0%   (0/2)
lockReadOnlyEnsureCurrent (Object []): void 0%   (0/1)0%   (0/8)0%   (0/2)
lockReadOnlyEnsureCurrent (Object [], int): void 0%   (0/1)0%   (0/8)0%   (0/2)
lockReadOnlyEnsureCurrent (Object): void 0%   (0/1)0%   (0/13)0%   (0/2)
lockReadOnlyEnsureCurrent (Object, SessionInfo): void 0%   (0/1)0%   (0/13)0%   (0/2)
lockReadOnlyEnsureCurrent (Object, int): void 0%   (0/1)0%   (0/13)0%   (0/2)
setProvider (SessionInfoProvider): void 0%   (0/1)0%   (0/4)0%   (0/2)
waitForUnlock (int): int 100% (1/1)68%  (30/44)73%  (11/15)
unlock (Object []): void 100% (1/1)82%  (80/98)85%  (17/20)
lock (int, long, long, List, SessionInfo, int, boolean, boolean): SessionInfo 100% (1/1)82%  (163/198)90%  (19.7/22)
unlockAll (int): void 100% (1/1)85%  (55/65)94%  (16/17)
unlock (int, long, long, LockMetaData): void 100% (1/1)85%  (138/163)94%  (29/31)
lock (int, long, long, LockMetaData, SessionInfo, int, boolean, boolean): Ses... 100% (1/1)88%  (369/419)97%  (80.6/83)
lock (Object [], SessionInfo, int, boolean, boolean): void 100% (1/1)89%  (199/223)94%  (44/47)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
LockTrackerImpl (): void 100% (1/1)100% (33/33)100% (10/10)
getClassLockEntry (Class): Set 100% (1/1)100% (67/67)100% (16/16)
getEntryName (LockMetaData): String 100% (1/1)100% (24/24)100% (1/1)
handle (PersistenceEvent): void 100% (1/1)100% (9/9)100% (3/3)
init (Map): void 100% (1/1)100% (12/12)100% (3/3)
lock (Object): void 100% (1/1)100% (13/13)100% (2/2)
lock (Object, int): void 100% (1/1)100% (13/13)100% (2/2)
lockEnsureCurrent (Object): void 100% (1/1)100% (13/13)100% (2/2)
lockReadOnly (Object): void 100% (1/1)100% (13/13)100% (2/2)
modifyIndirectEntries (Class, LockTrackerImpl$LockEntry, boolean): void 100% (1/1)100% (65/65)100% (16/16)
modifyIndirectEntries (LockTrackerImpl$LockEntry, boolean): void 100% (1/1)100% (7/7)100% (2/2)
release (): void 100% (1/1)100% (5/5)100% (2/2)
unlock (Object): void 100% (1/1)100% (9/9)100% (2/2)
unlock (int, long, long, List): void 100% (1/1)100% (72/72)100% (7/7)
     
class LockTrackerImpl$LockEntry100% (1/1)100% (1/1)100% (6/6)100% (1/1)
LockTrackerImpl$LockEntry (LockTrackerImpl): void 100% (1/1)100% (6/6)100% (1/1)

1/**
2 * Copyright (C) 2007 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.lock.impl;
20 
21import hu.netmind.beankeeper.service.StoreContext;
22import org.apache.log4j.Logger;
23import java.util.*;
24import hu.netmind.beankeeper.common.StoreException;
25import hu.netmind.beankeeper.event.PersistenceEventListener;
26import hu.netmind.beankeeper.event.PersistenceEvent;
27import hu.netmind.beankeeper.event.EventDispatcher;
28import hu.netmind.beankeeper.node.event.RemoteStateChangeEvent;
29import hu.netmind.beankeeper.lock.LockTracker;
30import hu.netmind.beankeeper.lock.SessionInfoProvider;
31import hu.netmind.beankeeper.lock.SessionInfo;
32import hu.netmind.beankeeper.transaction.Transaction;
33import hu.netmind.beankeeper.transaction.TransactionTracker;
34import hu.netmind.beankeeper.object.PersistenceMetaData;
35import hu.netmind.beankeeper.object.ObjectTracker;
36import hu.netmind.beankeeper.node.NodeManager;
37import hu.netmind.beankeeper.modification.ModificationTracker;
38 
39/**
40 * This implementation of a lock tracker uses transaction to make a preliminary check
41 * on the locks, then forwards calls to the central lock tracker.
42 * @author Brautigam Robert
43 * @version Revision: $Revision$
44 */
45public class LockTrackerImpl implements LockTracker, PersistenceEventListener
46{
47   private static Logger logger = Logger.getLogger(LockTrackerImpl.class);
48   
49   private SessionInfoProvider provider;
50   private EventDispatcher eventDispatcher = null; // Inject
51   private TransactionTracker transactionTracker = null; // Inject
52   private ObjectTracker objectTracker = null; // Inject
53   private NodeManager nodeManager = null; // Inject
54   private ModificationTracker modificationTracker = null; // Injected
55 
56   public void init(Map parameters)
57   {
58      this.provider=new TransactionInfoProvider(transactionTracker);
59      eventDispatcher.registerListener(this);
60   }
61 
62   public void release()
63   {
64      eventDispatcher.unregisterListener(this);
65   }
66 
67   public SessionInfoProvider getProvider()
68   {
69      return provider;
70   }
71   public void setProvider(SessionInfoProvider provider)
72   {
73      this.provider=provider;
74   }
75 
76   /**
77    * Lock a single object, and ensure that the object given is the
78    * most recent version of the object.  The session information is gathered
79    * from the session info provider.
80    * @throws ConcurrentModificationException If the object is already
81    * locked by another thread.
82    */
83   public void lockEnsureCurrent(Object obj)
84   {
85      lock(new Object[] { obj },null,-1, true, false);
86   }
87 
88   /**
89    * Lock a single object. The session information is gathered
90    * from the session info provider.
91    * @throws ConcurrentModificationException If the object is already
92    * locked by another thread.
93    */
94   public void lock(Object obj)
95   {
96      lock(new Object[] { obj },null,-1, false, false);
97   }
98 
99   /**
100    * Lock a single object, and guarantee it's current. 
101    * The session information is gathered from the session info provider.
102    * @param obj The object to lock.
103    * @param wait Wait the given amount of milliseconds for the lock to
104    * free up. Method only throws ConcurrentModificationException if
105    * the lock is not available in the given time.
106    * @throws ConcurrentModificationException If the object is already
107    * locked by another thread.
108    */
109   public void lockEnsureCurrent(Object obj,int wait)
110   {
111      lock(new Object[] { obj },null,wait, true, false);
112   }
113 
114   /**
115    * Lock a single object with wait period given. The session information is gathered
116    * from the session info provider.
117    * @param obj The object to lock.
118    * @param wait Wait the given amount of milliseconds for the lock to
119    * free up. Method only throws ConcurrentModificationException if
120    * the lock is not available in the given time.
121    * @throws ConcurrentModificationException If the object is already
122    * locked by another thread.
123    */
124   public void lock(Object obj,int wait)
125   {
126      lock(new Object[] { obj },null,wait, false, false);
127   }
128 
129   /**
130    * Lock a single object with the session information given,
131    * and check is the given object is the current version.
132    * @throws ConcurrentModificationException If the object is already
133    * locked by another thread.
134    */
135   public void lockEnsureCurrent(Object obj, SessionInfo info)
136   {
137      lock(new Object[] { obj }, info, -1, true, false);
138   }
139   
140   /**
141    * Lock a single object with the session information given.
142    * @throws ConcurrentModificationException If the object is already
143    * locked by another thread.
144    */
145   public void lock(Object obj, SessionInfo info)
146   {
147      lock(new Object[] { obj }, info, -1, false, false);
148   }
149   
150   /**
151    * Lock multiple objects, and check whether given objects
152    * are current. The session information is gathered
153    * from the session info provider. Use this method, if you want
154    * to lock multiple objects at the same time.
155    * @throws ConcurrentModificationException If the object is already
156    * locked by another thread.
157    */
158   public void lockEnsureCurrent(Object[] objs)
159   {
160      lock(objs,null,-1, true, false);
161   }
162  
163   /**
164    * Lock multiple objects. The session information is gathered
165    * from the session info provider. Use this method, if you want
166    * to lock multiple objects at the same time.
167    * @throws ConcurrentModificationException If the object is already
168    * locked by another thread.
169    */
170   public void lock(Object[] objs)
171   {
172      lock(objs,null,-1, false, false);
173   }
174  
175   /**
176    * Lock multiple objects, and check whether given objects
177    * are current. The session information is gathered
178    * from the session info provider. Use this method, if you want
179    * to lock multiple objects at the same time.
180    * @param wait Wait the given amount of milliseconds for the lock to
181    * free up. Method only throws ConcurrentModificationException if
182    * the lock is not available in the given time.
183    * @throws ConcurrentModificationException If the object is already
184    * locked by another thread.
185    */
186   public void lockEnsureCurrent(Object[] objs, int wait)
187   {
188      lock(objs,null,wait,true, false);
189   }
190  
191   /**
192    * Lock multiple objects. The session information is gathered
193    * from the session info provider. Use this method, if you want
194    * to lock multiple objects at the same time.
195    * @param wait Wait the given amount of milliseconds for the lock to
196    * free up. Method only throws ConcurrentModificationException if
197    * the lock is not available in the given time.
198    * @throws ConcurrentModificationException If the object is already
199    * locked by another thread.
200    */
201   public void lock(Object[] objs, int wait)
202   {
203      lock(objs,null,wait,false, false);
204   }
205  
206   /**
207    * Lock a single object read-only, and ensure that the object given is the
208    * most recent version of the object.  The session information is gathered
209    * from the session info provider.
210    * @throws ConcurrentModificationException If the object is already
211    * locked by another thread.
212    */
213   public void lockReadOnlyEnsureCurrent(Object obj)
214   {
215      lock(new Object[] { obj },null,-1, true, true);
216   }
217 
218   /**
219    * Lock a single object read-only. The session information is gathered
220    * from the session info provider.
221    * @throws ConcurrentModificationException If the object is already
222    * locked by another thread.
223    */
224   public void lockReadOnly(Object obj)
225   {
226      lock(new Object[] { obj },null,-1, false, true);
227   }
228 
229   /**
230    * Lock a single object read-only, and guarantee it's current. 
231    * The session information is gathered from the session info provider.
232    * @param obj The object to lock.
233    * @param wait Wait the given amount of milliseconds for the lock to
234    * free up. Method only throws ConcurrentModificationException if
235    * the lock is not available in the given time.
236    * @throws ConcurrentModificationException If the object is already
237    * locked by another thread.
238    */
239   public void lockReadOnlyEnsureCurrent(Object obj,int wait)
240   {
241      lock(new Object[] { obj },null,wait, true, true);
242   }
243 
244   /**
245    * Lock a single object read-only with wait period given. The session information is gathered
246    * from the session info provider.
247    * @param obj The object to lock.
248    * @param wait Wait the given amount of milliseconds for the lock to
249    * free up. Method only throws ConcurrentModificationException if
250    * the lock is not available in the given time.
251    * @throws ConcurrentModificationException If the object is already
252    * locked by another thread.
253    */
254   public void lockReadOnly(Object obj,int wait)
255   {
256      lock(new Object[] { obj },null,wait, false, true);
257   }
258 
259   /**
260    * Lock a single object read-only with the session information given,
261    * and check is the given object is the current version.
262    * @throws ConcurrentModificationException If the object is already
263    * locked by another thread.
264    */
265   public void lockReadOnlyEnsureCurrent(Object obj, SessionInfo info)
266   {
267      lock(new Object[] { obj }, info, -1, true, true);
268   }
269   
270   /**
271    * Lock a single object read-only with the session information given.
272    * @throws ConcurrentModificationException If the object is already
273    * locked by another thread.
274    */
275   public void lockReadOnly(Object obj, SessionInfo info)
276   {
277      lock(new Object[] { obj }, info, -1, false, true);
278   }
279   
280   /**
281    * Lock multiple objects read-only, and check whether given objects
282    * are current. The session information is gathered
283    * from the session info provider. Use this method, if you want
284    * to lock multiple objects at the same time.
285    * @throws ConcurrentModificationException If the object is already
286    * locked by another thread.
287    */
288   public void lockReadOnlyEnsureCurrent(Object[] objs)
289   {
290      lock(objs,null,-1, true, true);
291   }
292  
293   /**
294    * Lock multiple objects read-only. The session information is gathered
295    * from the session info provider. Use this method, if you want
296    * to lock multiple objects at the same time.
297    * @throws ConcurrentModificationException If the object is already
298    * locked by another thread.
299    */
300   public void lockReadOnly(Object[] objs)
301   {
302      lock(objs,null,-1, false, true);
303   }
304  
305   /**
306    * Lock multiple objects read-only, and check whether given objects
307    * are current. The session information is gathered
308    * from the session info provider. Use this method, if you want
309    * to lock multiple objects at the same time.
310    * @param wait Wait the given amount of milliseconds for the lock to
311    * free up. Method only throws ConcurrentModificationException if
312    * the lock is not available in the given time.
313    * @throws ConcurrentModificationException If the object is already
314    * locked by another thread.
315    */
316   public void lockReadOnlyEnsureCurrent(Object[] objs, int wait)
317   {
318      lock(objs,null,wait,true, true);
319   }
320  
321   /**
322    * Lock multiple objects read-only. The session information is gathered
323    * from the session info provider. Use this method, if you want
324    * to lock multiple objects at the same time.
325    * @param wait Wait the given amount of milliseconds for the lock to
326    * free up. Method only throws ConcurrentModificationException if
327    * the lock is not available in the given time.
328    * @throws ConcurrentModificationException If the object is already
329    * locked by another thread.
330    */
331   public void lockReadOnly(Object[] objs, int wait)
332   {
333      lock(objs,null,wait,false, true);
334   }
335  
336   /**
337    * Lock multiple objects with all possible parameters specified.
338    * Use this method, if you want to lock multiple objects at the same time.
339    * If classes are asked to be ensured to be current, the following date is
340    * taken into account:
341    * <ul>
342    *    <li>If the lock is called from inside a transaction, the
343    *    transaction's first operation's date is taken.</li>
344    *    <li>If there are regular objects with this lock call, then
345    *    the oldest object's read date is taken.</li>
346    * </ul>
347    * Whichever was earlier, the classes are checked against that date.
348    * @param objs The object to lock simultaniously.
349    * @param info The session info to memorize for this lock. This object will
350    * be included in the ConcurrentModificationException.
351    * @param wait Wait the given amount of milliseconds for the lock to
352    * free up. Method only throws ConcurrentModificationException if
353    * the lock is not available in the given time.
354    * @param ensureCurrent Do the objects need to be guaranteed to be current.
355    * If this flag is set, the lock operation will fail, if any supplied
356    * object has a newer version.
357    * @param readOnly Whether the lock should be read-only. If a lock is read-only, then
358    * other read-only locks can still be established on the specified objects, read-write
359    * locks will however fail.
360    * @throws ConcurrentModificationException If the object is already
361    * locked by another process.
362    */
363   public synchronized void lock(Object[] objs, SessionInfo info, int wait, boolean ensureCurrent, boolean readOnly)
364   {
365      if ( logger.isDebugEnabled() )
366         logger.debug("locking "+objs.length+" objects, info: "+info+", wait: "+wait);
367      // Allocate session info
368      if ( info == null )
369      {
370         info = new SessionInfo(); // Empty
371         try
372         {
373            if ( provider != null )
374               info = provider.getSessionInfo();
375         } catch ( Throwable e ) {
376            logger.warn("could not allocate session info from provider",e);
377         }
378      }
379      // If this lock operation is inside a transacion,
380      // then the start of the transaction will be the
381      // date the classes (tables) are ensure to be current.
382      // So if inside a transaction a class is asked to be
383      // ensured actual, then it's ensured that it's not
384      // been tampered with since the beginning of the transacion.
385      Transaction tx = transactionTracker.getTransaction(
386            TransactionTracker.TX_OPTIONAL);
387      Long firstSerial = null;
388      long txSerial = 0;
389      if ( tx != null )
390      {
391         firstSerial = tx.getSerial();
392         logger.debug("there was a transaction, classes will be compared to at least: "+firstSerial);
393         if ( tx.getSerial() != null )
394            txSerial = tx.getSerial().longValue();
395      }
396      // Create metadata for all objects
397      ArrayList remoteLockMetas = new ArrayList();
398      for ( int i=0; i<objs.length; i++ )
399      {
400         LockMetaData meta = new LockMetaData();
401         if ( objs[i] instanceof Class )
402         {
403            // Classes
404            meta.setObjectClass((Class)objs[i]);
405         } else {
406            // Objects
407            meta.setObjectClass(objs[i].getClass());
408            PersistenceMetaData persistenceMeta = objectTracker.getMetaData(objs[i]);
409            meta.setPersistenceId(persistenceMeta.getPersistenceId());
410            meta.setLastCurrentSerial(persistenceMeta.getLastCurrentSerial());
411            meta.setPersistenceStart(persistenceMeta.getPersistenceStart());
412            meta.setPersistenceEnd(persistenceMeta.getPersistenceEnd());
413            // Check if this query serial is older than the first date current
414            if ( (meta.getLastCurrentSerial()!=null) && ((firstSerial==null) || (firstSerial.longValue() > meta.getLastCurrentSerial().longValue())) )
415               firstSerial = meta.getLastCurrentSerial();
416         }
417         remoteLockMetas.add(meta);
418      }
419      // Re-set the oldest serial to the class-only metas
420      for ( int i=0; i<remoteLockMetas.size(); i++ )
421      {
422         LockMetaData meta = (LockMetaData) remoteLockMetas.get(i);
423         if ( meta.getPersistenceId() == null )
424            meta.setLastCurrentSerial(firstSerial);
425      }
426      // Now make an ordered list of the remote lock objects. It should be
427      // ordered, because that reduces the likelyhood of a deadlock.
428      Collections.sort(remoteLockMetas);
429      // Lock with central lock tracker
430      SessionInfo oldInfo = lock(nodeManager.getId(),
431            Thread.currentThread().getId(),txSerial,remoteLockMetas,info,wait,ensureCurrent,readOnly);
432      if ( oldInfo != null )
433         throw new hu.netmind.beankeeper.lock.ConcurrentModificationException(oldInfo,objs,"Tried to lock, but was already locked.");
434      // If object could be locked, and it ensured that those objects
435      // were current, then set the last known current serial to current
436      if ( tx != null )
437      {
438         Long serial = tx.getSerial();
439         for ( int i=0; i<objs.length; i++ )
440         {
441            if ( objs[i] instanceof Class )
442               continue;
443            objectTracker.updateCurrent(objs[i],serial);
444         }
445      }
446      // All is good in lockworld
447      logger.debug("successful lock operation.");
448   }
449 
450   /**
451    * Unlock a single object. If the object is not locked, nothing is done.
452    */
453   public void unlock(Object obj)
454   {
455      unlock(new Object[] { obj });
456   }
457 
458   /**
459    * Unlock multiple objects.
460    */
461   public synchronized void unlock(Object[] objs)
462   {
463      if ( logger.isDebugEnabled() )
464         logger.debug("unlocking "+objs.length+" objects.");
465      // The transaction if there is any
466      Transaction tx = transactionTracker.getTransaction(
467            TransactionTracker.TX_OPTIONAL);
468      long txSerial = 0;
469      if ( (tx!=null) && (tx.getSerial()!=null) )
470         txSerial = tx.getSerial().longValue();
471      // Go through all objects and create lock metadata
472      ArrayList remoteLockMetas = new ArrayList();
473      for ( int i=0; i<objs.length; i++ )
474      {
475         LockMetaData meta = new LockMetaData();
476         if ( objs[i] instanceof Class )
477         {
478            // Classes
479            meta.setObjectClass((Class)objs[i]);
480         } else {
481            // Objects
482            meta.setObjectClass(objs[i].getClass());
483            PersistenceMetaData persistenceMeta = objectTracker.getMetaData(objs[i]);
484            meta.setPersistenceId(persistenceMeta.getPersistenceId());
485         }
486         remoteLockMetas.add(meta);
487      }
488      // Remote unlock
489      try
490      {
491         unlock(nodeManager.getId(),
492               Thread.currentThread().getId(),txSerial,remoteLockMetas);
493      } catch ( Exception e ) {
494         // Do not throw excetion here, because unlock only fails,
495         // if connection is severed, in which case the server will
496         // invalidate all locks either way.
497         logger.warn("could not unlock remotely",e);
498      }
499   }
500   
501   /*
502    * Remote part of the service.
503    * Tracks remote locks. There are two views of tracked data: the index
504    * of the node it came from, and the object's id itself. There is also
505    * a transaction associated with the lock.
506    */
507   private HashMap lockEntriesByName;
508   private HashMap lockEntriesByIndex;
509   private HashMap indirectLockEntriesByClass;
510 
511   public LockTrackerImpl()
512   {
513      lockEntriesByName = new HashMap();
514      lockEntriesByIndex = new HashMap();
515      indirectLockEntriesByClass = new HashMap();
516   }
517 
518   private void modifyIndirectEntries(LockEntry entry, boolean add)
519   {
520      modifyIndirectEntries(entry.objectClass,entry,add);
521   }
522 
523   private void modifyIndirectEntries(Class currentClass, LockEntry entry, boolean add)
524   {
525      // If there is no class, then return
526      if ( currentClass == null )
527         return;
528      // Mark class
529      Set entries = (Set) indirectLockEntriesByClass.get(currentClass);
530      if ( entries == null )
531      {
532         entries = new HashSet();
533         indirectLockEntriesByClass.put(currentClass,entries);
534      }
535      if ( add )
536      {
537         // Add entry to indicate that it indirectly uses this class
538         entries.add(entry);
539      } else {
540         // Remove that entry
541         entries.remove(entry);
542         if ( entries.size() == 0 )
543            indirectLockEntriesByClass.remove(currentClass);
544      }
545      // Mark superclass
546      modifyIndirectEntries(currentClass.getSuperclass(),entry,add);
547      // Mark interfaces
548      Class[] interfaces = currentClass.getInterfaces();
549      for ( int i=0; i<interfaces.length; i++ )
550         modifyIndirectEntries(interfaces[i],entry,add);
551   }
552   
553   public void handle(PersistenceEvent event)
554   {
555      if ( event instanceof RemoteStateChangeEvent )
556         unlockAll(((RemoteStateChangeEvent)event).getNodeId());
557   }
558 
559   private synchronized void unlockAll(int index)
560   {
561      if ( logger.isDebugEnabled() )
562         logger.debug("unlocking all from: "+index);
563      Set entries = (Set) lockEntriesByIndex.remove(new Integer(index));
564      if ( entries == null )
565         return;
566      Iterator entriesIterator = entries.iterator();
567      while ( entriesIterator.hasNext() )
568      {
569         LockEntry entry = (LockEntry) entriesIterator.next();
570         Set nameEntries = (Set) lockEntriesByName.get(entry.name);
571         if ( nameEntries != null )
572         {
573            nameEntries.remove(entry);
574            if ( nameEntries.isEmpty() )
575               lockEntriesByName.remove(entry.name);
576         }
577         modifyIndirectEntries(entry,false);
578      }
579      // Notify waiting threads, that locks became unlocked.
580      notifyAll();
581   }
582 
583   /**
584    * Wait a given amount of time for an unlock event.
585    * @return The new wait interval that's left of the input wait period, or -1 if during
586    * the wait period no unlock events were generated.
587    */
588   private int waitForUnlock(int wait)
589   {
590      if ( wait < 0 )
591         throw new StoreException("wait called with negative wit period, this should not happen");
592      // Start wait
593      long startTime = System.currentTimeMillis();
594      try
595      {
596         // Wait for unlock notification
597         if ( wait > 0 )
598            wait(wait); 
599         else
600            wait();
601      } catch ( Throwable e ) {
602         throw new StoreException("wait interrupted",e);
603      }
604      if ( wait > 0 )
605      {
606         long endTime = System.currentTimeMillis();
607         wait -= (endTime-startTime);
608         if ( wait <= 0 ) // Would mean infinite
609            wait = -1; // Expired instead
610      }
611      // Return the modified time
612      return wait;
613   }
614 
615   /**
616    * Get the lock entries for a class or any of it's superclasses or superinterfaces.
617    */
618   private Set getClassLockEntry(Class currentClass)
619   {
620      if ( currentClass == null )
621         return null;
622      logger.debug("checking lock entry for class: "+currentClass);
623      // Check class
624      Set result = new HashSet();
625      Set entries = (Set) lockEntriesByName.get(currentClass.getName());
626      if ( entries != null )
627         result.addAll(entries);
628      // Check superclass
629      entries = getClassLockEntry(currentClass.getSuperclass());
630      if ( entries != null )
631         result.addAll(entries);
632      // Check interfaces
633      Class[] interfaces = currentClass.getInterfaces();
634      for ( int i=0; i<interfaces.length; i++ )
635      {
636         entries = getClassLockEntry(interfaces[i]);
637         if ( entries != null )
638            result.addAll(entries);
639      }
640      // Fallback
641      return result;
642   }
643 
644   /**
645    * Get the composite name for a lock metadata.
646    */
647   private String getEntryName(LockMetaData meta)
648   {
649      return meta.getObjectClass().getName()+(meta.getPersistenceId()==null?"":(":"+meta.getPersistenceId()));
650   }
651 
652   private SessionInfo lock(int index, long threadId, long txSerial, LockMetaData meta, SessionInfo info, int wait, boolean ensureCurrent, boolean readOnly)
653   {
654      if ( logger.isDebugEnabled() )
655         logger.debug("locking from: "+index+":"+threadId+":"+txSerial+", meta: "+meta+", info: "+info+", wait: "+wait+", ensure current: "+ensureCurrent);
656      // Check whether object can have a lock engaged. To do this we require the
657      // following checks _in a single interation_, because if we yield to another
658      // thread, the situation may change, and every check has to be made again.
659      // The checks:
660      // - Check if lock is explicitly given using lockEntriesByName (if object)
661      // - Check if lock explicitly set for any class or superclass. 
662      // - Check whether class has implicit usage by other lock (if class).
663      boolean allChecksOk = false;
664      LockEntry ownedEntry = null;
665      while ( ! allChecksOk )
666      {
667         LockEntry lockingEntry = null;
668         // Check whether object is locked
669         if ( meta.getPersistenceId() != null )
670         {
671            Set entries = (Set) lockEntriesByName.get(getEntryName(meta));
672            if ( (entries!=null) && (!entries.isEmpty()) )
673            {
674               Iterator entriesIterator = entries.iterator();
675               while ( (entriesIterator.hasNext()) && (lockingEntry==null) )
676               {
677                  LockEntry entry = (LockEntry) entriesIterator.next();
678                  if ( (entry.index == index) && (entry.threadId==threadId) &&
679                    ((entry.txSerial==0) || (txSerial==0) || (entry.txSerial==txSerial)) )
680                  {
681                     ownedEntry = entry;
682                  } else {
683                     if ( (!readOnly) || (!entry.readOnly) )
684                     {
685                        logger.debug("lock is present from another node or thread, and either that or this lock should be exclusive, lock unsuccessful.");
686                        lockingEntry = entry;
687                     }
688                  }
689               }
690            } else {
691               logger.debug("no lock for object is present.");
692            }
693         }
694         // Check class and superclasses, whether they are locked
695         if ( lockingEntry == null )
696         {
697            Set entries = getClassLockEntry(meta.getObjectClass());
698            Iterator entriesIterator = entries.iterator();
699            while ( (entriesIterator.hasNext()) && (lockingEntry==null) )
700            {
701               LockEntry entry = (LockEntry) entriesIterator.next();
702               if ((! ( (entry.index == index) && (entry.threadId==threadId) &&
703                    ((entry.txSerial==0) || (txSerial==0) || (entry.txSerial==txSerial)) ) ) &&
704                   (!(entry.readOnly && readOnly)) )
705                  lockingEntry = entry;
706            }
707         }
708         // If this is a class, then check, if it's indirectly used by
709         // already activated locks, which are not owned by this thread.
710         if ( (lockingEntry==null) && (meta.getPersistenceId() == null) )
711         {
712            Set entries = (Set) indirectLockEntriesByClass.get(meta.getObjectClass());
713            if ( entries != null )
714            {
715               Iterator entriesIterator = entries.iterator();
716               while ( (lockingEntry==null) && (entriesIterator.hasNext()))
717               {
718                  LockEntry entry = (LockEntry) entriesIterator.next();
719                  if ((! ( (entry.index == index) && (entry.threadId==threadId) &&
720                        ((entry.txSerial==0) || (txSerial==0) || (entry.txSerial==txSerial)) ) ) &&
721                      (!(entry.readOnly && readOnly)) )
722                     lockingEntry = entry;
723               }
724            }
725         }
726         // If a locking entry is found, then try to wait
727         if ( lockingEntry != null )
728         {
729            if ( wait < 0 )
730            {
731               // Tried to lock from another node or another thread in the same node
732               logger.debug("object already locked: "+getEntryName(meta)+", from: "+lockingEntry.index+":"+lockingEntry.threadId+":"+lockingEntry.txSerial);
733               return lockingEntry.info;
734            } else {
735               // Wait for an unlock event
736               wait = waitForUnlock(wait);
737            }
738            allChecksOk = false;
739         } else {
740            allChecksOk = true;
741         }
742      }
743      // The lock is ready to be engaged. Now we check (if necessary)
744      // if the object is current. If it is not, we can still throw
745      // an exception, because the lock is not yet inserted in the
746      // datamodel. On the other hand, the object is surely not locked,
747      // or owned by caller, so it's sure, there won't be any operations
748      // on the object now, during this call.
749      if ( ensureCurrent )
750      {
751         boolean current = false;
752         if ( meta.getPersistenceId()==null )
753         {
754            // This means target is a class
755            current = modificationTracker.isCurrent(
756                  meta.getObjectClass(),meta.getLastCurrentSerial());
757         } else {
758            // Meta describes an object
759            current = modificationTracker.isCurrent(meta);
760         }
761         if ( ! current )
762         {
763            logger.debug("object was not current, returning session");
764            return new SessionInfo(); // Return empty session info
765         }
766      }
767      // If entry is already present, then just adjust
768      if ( ownedEntry != null )
769      {
770         if ( logger.isDebugEnabled() )
771            logger.debug("there is already a lock for that object, but from the same node"+
772                 " and thread, and no other locks (or just read-only locks) present, lock success.");
773         // Lock comes from the same node, and from the same thread,
774         // so it's the owner of the lock. Increase depth level.
775         ownedEntry.lockDepth++;
776         // Adjust readonly flag
777         ownedEntry.readOnly &= readOnly;
778         return null;
779      }
780      // Create lock
781      LockEntry entry = new LockEntry();
782      entry.id=meta.getPersistenceId();
783      entry.objectClass=meta.getObjectClass();
784      entry.name=getEntryName(meta);
785      entry.index=index;
786      entry.threadId=threadId;
787      entry.txSerial=txSerial;
788      entry.info=info;
789      entry.lockDepth=1;
790      entry.readOnly=readOnly;
791      entry.lastModificationTime=System.currentTimeMillis();
792      // Update data model
793      Set entries = (Set) lockEntriesByIndex.get(new Integer(index));
794      if ( entries == null )
795      {
796         entries = new HashSet();
797         lockEntriesByIndex.put(new Integer(index),entries);
798      }
799      entries.add(entry);
800      Set nameEntries = (Set) lockEntriesByName.get(getEntryName(meta));
801      if ( nameEntries == null )
802      {
803         nameEntries = new HashSet();
804         lockEntriesByName.put(getEntryName(meta),nameEntries);
805      }
806      nameEntries.add(entry);
807      modifyIndirectEntries(entry,true);
808      // Return
809      logger.debug("new lock successfully created");
810      return null;
811   }
812 
813   private void unlock(int index, long threadId, long txSerial, LockMetaData meta)
814   {
815      if ( logger.isDebugEnabled() )
816         logger.debug("unlocking from: "+index+":"+threadId+":"+txSerial+", name: "+getEntryName(meta));
817      // Get the entry first. Note: owner is the same
818      // if it's the same index and thread. Transactions do not matter here.
819      Set nameEntries = (Set) lockEntriesByName.get(getEntryName(meta));
820      LockEntry entry = null;
821      if ( nameEntries != null )
822      {
823         Iterator nameEntriesIterator = nameEntries.iterator();
824         while ( (nameEntriesIterator.hasNext()) && (entry==null) )
825         {
826            LockEntry tmp = (LockEntry) nameEntriesIterator.next();
827            if ( (tmp.index==index) && (tmp.threadId==threadId) )
828               entry=tmp;
829         }
830      }
831      // Check if it's there
832      if ( entry == null )
833      {
834         logger.warn("unlocking from: "+index+":"+threadId+":"+txSerial+", name: "+getEntryName(meta));
835         logger.warn("no lock present, altough tried to unlock, possible unbalanced lock-unlock in code.");
836         return; // No lock present
837      }
838      // Decrease lock depth
839      entry.lockDepth--;
840      if ( entry.lockDepth > 0 )
841      {
842         logger.debug("lock depth decreased to: "+entry.lockDepth);
843         return; // No unlock yet
844      }
845      // Full remove of the entry
846      Set entries = (Set) lockEntriesByIndex.get(new Integer(index));
847      if ( entries == null )
848         return;
849      entries.remove(entry);
850      if ( entries.size() == 0 )
851         lockEntriesByIndex.remove(new Integer(index));
852      nameEntries.remove(entry);
853      if ( nameEntries.isEmpty() )
854         lockEntriesByName.remove(getEntryName(meta));
855      modifyIndirectEntries(entry,false);
856      logger.debug("successful unlock operation");
857   }
858 
859   /**
860    * Lock given ids.
861    */
862   public synchronized SessionInfo lock(int index, long threadId, long txSerial, 
863         List metas, SessionInfo info, int wait, boolean ensureCurrent, boolean readOnly)
864   {
865      // Make it a server call
866      if ( nodeManager.getRole() == NodeManager.NodeRole.CLIENT )
867      {
868         logger.debug("lock on client, sending to server");
869         return (SessionInfo) nodeManager.callServer(LockTracker.class.getName(),
870               "lock",new Class[] { int.class, long.class, long.class, 
871               List.class, SessionInfo.class, int.class, boolean.class, boolean.class },
872               new Object[] { index, threadId, txSerial, metas, info, wait, ensureCurrent, readOnly });
873      }
874      // Local
875      ArrayList lockedMetas = new ArrayList();
876      // Go through all ids, and lock'em all
877      SessionInfo result = null;
878      boolean ended = false;
879      try
880      {
881         for ( int i=0; (i<metas.size()) && (result==null); i++ )
882         {
883            LockMetaData meta = (LockMetaData) metas.get(i);
884            long startTime = System.currentTimeMillis();
885            result = lock(index,threadId,txSerial,meta,info,wait,ensureCurrent,readOnly);
886            if ( wait > 0 )
887            {
888               long endTime = System.currentTimeMillis();
889               wait -= (endTime-startTime);
890               if ( wait <= 0 ) // Would mean infinite
891                  wait = -1; // Expired instead
892            }
893            if ( result == null )
894               lockedMetas.add(meta);
895         }
896         ended = true;
897      } finally {
898         if ( (!ended) || (result!=null) )
899         {
900            // Some error happened, or a lock could not be established,
901            // so unlock all successfully locked ones, because either
902            // all locks are established, or none of them when the method
903            // returns.
904            for ( int o=0; o<lockedMetas.size(); o++ )
905               unlock(index, threadId, txSerial, (LockMetaData) lockedMetas.get(o) );
906         }
907      }
908      return result;
909   }
910 
911   /**
912    * Unlock given ids.
913    */
914   public synchronized void unlock(int index, long threadId, long txSerial, List metas)
915   {
916      // Make it a server call
917      if ( nodeManager.getRole() == NodeManager.NodeRole.CLIENT )
918      {
919         nodeManager.callServer(LockTracker.class.getName(),
920               "unlock",new Class[] { int.class, long.class, long.class, List.class },
921               new Object[] { index, threadId, txSerial, metas });
922         return;
923      }
924      // Go through all ids, and unlock'em all
925      for ( int i=0; i<metas.size(); i++ )
926         unlock(index,threadId,txSerial,(LockMetaData) metas.get(i));
927      // Notify waiting threads, that locks became unlocked.
928      notifyAll();
929   }
930 
931 
932   public class LockEntry
933   {
934      public Long id;
935      public String name;
936      public Class objectClass;
937 
938      public long threadId;
939      public long txSerial;
940      public int index;
941 
942      public SessionInfo info;
943      public long lastModificationTime;
944      public int lockDepth;
945      public boolean readOnly;
946   }
947 
948}
949 
950 

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