Coverage Report - org.apache.ojb.odmg.locking.InMemoryLockMapImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
InMemoryLockMapImpl
N/A
N/A
2.318
 
 1  
 package org.apache.ojb.odmg.locking;
 2  
 
 3  
 /* Copyright 2002-2005 The Apache Software Foundation
 4  
  *
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 import org.apache.ojb.broker.Identity;
 19  
 import org.apache.ojb.broker.PersistenceBroker;
 20  
 import org.apache.ojb.broker.util.configuration.Configuration;
 21  
 import org.apache.ojb.broker.util.configuration.ConfigurationException;
 22  
 import org.apache.ojb.odmg.TransactionImpl;
 23  
 import org.apache.ojb.odmg.TxManagerFactory;
 24  
 
 25  
 import java.util.Collection;
 26  
 import java.util.HashMap;
 27  
 import java.util.Iterator;
 28  
 import java.util.Map;
 29  
 import java.util.Vector;
 30  
 
 31  
 /**
 32  
  *
 33  
  * We use a HashMap and synchronize blocks of access for a get "check" then put
 34  
  * operation. We cannot use the hashtable as you could check in one synchronized call
 35  
  * then put in another while a different thread is doing the same thing.
 36  
  *
 37  
  * @deprecated
 38  
  * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird<a>
 39  
  *                 update for use of Hashmap with synchronization.
 40  
  *                 implemented timed out lock removal algo.
 41  
  * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
 42  
  *                 original author.
 43  
  * @version $Id: InMemoryLockMapImpl.java,v 1.1 2007-08-24 22:17:28 ewestfal Exp $
 44  
  */
 45  
 public class InMemoryLockMapImpl implements LockMap
 46  
 {
 47  
         /**
 48  
          * MBAIRD: a LinkedHashMap returns objects in the order you put them in,
 49  
          * while still maintaining an O(1) lookup like a normal hashmap. We can then
 50  
          * use this to get the oldest entries very quickly, makes cleanup a breeze.
 51  
          */
 52  
     private HashMap locktable = new HashMap();
 53  
 
 54  
     private long m_lastCleanupAt = System.currentTimeMillis();
 55  
         private static long CLEANUP_FREQUENCY = 500; // 500 milliseconds.
 56  
         private static int MAX_LOCKS_TO_CLEAN = 50;
 57  
 
 58  
     /**
 59  
      * returns the LockEntry for the Writer of object obj.
 60  
      * If now writer exists, null is returned.
 61  
      */
 62  
     public LockEntry getWriter(Object obj)
 63  
     {
 64  
         PersistenceBroker broker = getBroker();
 65  
         Identity oid = new Identity(obj, broker); 
 66  
         return getWriter(oid);
 67  
     }
 68  
 
 69  
     public LockEntry getWriter(Identity oid)
 70  
     {
 71  
         checkTimedOutLocks();
 72  
         /* TODO: smarter solution in future */
 73  
         // fix/workaround
 74  
         // When Identity needs new id's we must overgive
 75  
         // the a target broker when run with multiple databases
 76  
         // using H/L sequence manager
 77  
         ObjectLocks objectLocks = null;
 78  
         synchronized(locktable)
 79  
         {
 80  
             objectLocks = (ObjectLocks) locktable.get(oid.toString());
 81  
         }
 82  
         if (objectLocks == null)
 83  
         {
 84  
             return null;
 85  
         }
 86  
         else
 87  
         {
 88  
             return objectLocks.getWriter();
 89  
         }
 90  
     }
 91  
 
 92  
     /**
 93  
      * obtain a PersistenceBroker instance for persistence operations.
 94  
      */
 95  
     private PersistenceBroker getBroker()
 96  
     {
 97  
         return TxManagerFactory.instance().getCurrentTransaction().getBroker();
 98  
     }
 99  
 
 100  
     /**
 101  
      * returns a collection of Reader LockEntries for object obj.
 102  
      * If no LockEntries could be found an empty Vector is returned.
 103  
      */
 104  
     public Collection getReaders(Object obj)
 105  
     {
 106  
             checkTimedOutLocks();
 107  
         Identity oid = new Identity(obj,getBroker());
 108  
         return getReaders(oid);
 109  
     }
 110  
 
 111  
     public Collection getReaders(Identity oid)
 112  
     {
 113  
         ObjectLocks objectLocks = null;
 114  
         synchronized (locktable)
 115  
         {
 116  
                 objectLocks = (ObjectLocks) locktable.get(oid.toString());
 117  
         }
 118  
         if (objectLocks == null)
 119  
         {
 120  
             return new Vector();
 121  
         }
 122  
         else
 123  
         {
 124  
             return objectLocks.getReaders().values();
 125  
         }
 126  
     }
 127  
 
 128  
     /**
 129  
      * Add a reader lock entry for transaction tx on object obj
 130  
      * to the persistent storage.
 131  
      */
 132  
     public boolean addReader(TransactionImpl tx, Object obj)
 133  
     {
 134  
         checkTimedOutLocks();
 135  
 
 136  
         Identity oid = new Identity(obj,getBroker());
 137  
         LockEntry reader = new LockEntry(oid.toString(),
 138  
                 tx.getGUID(),
 139  
                 System.currentTimeMillis(),
 140  
                 LockStrategyFactory.getIsolationLevel(obj),
 141  
                 LockEntry.LOCK_READ);
 142  
 
 143  
         addReaderInternal(reader);
 144  
         return true;
 145  
     }
 146  
 
 147  
     void addReaderInternal(LockEntry reader)
 148  
     {
 149  
         ObjectLocks objectLocks = null;
 150  
         /**
 151  
          * MBAIRD: We need to synchronize the get/put so we don't have two threads
 152  
          * competing to check if something is locked and double-locking it.
 153  
          */
 154  
         synchronized (locktable)
 155  
         {
 156  
                 String oidString = reader.getOidString();
 157  
                 objectLocks = (ObjectLocks) locktable.get(oidString);
 158  
             if (objectLocks == null)
 159  
             {
 160  
                 objectLocks = new ObjectLocks();
 161  
                 locktable.put(oidString, objectLocks);
 162  
             }
 163  
         }
 164  
         objectLocks.addReader(reader);
 165  
     }
 166  
 
 167  
     /**
 168  
      * remove a reader lock entry for transaction tx on object obj
 169  
      * from the persistent storage.
 170  
      */
 171  
     public void removeReader(TransactionImpl tx, Object obj)
 172  
     {
 173  
         checkTimedOutLocks();
 174  
 
 175  
         Identity oid = new Identity(obj, getBroker());
 176  
         String oidString = oid.toString();
 177  
         String txGuid = tx.getGUID();
 178  
         removeReaderInternal(oidString, txGuid);
 179  
     }
 180  
 
 181  
     private void removeReaderInternal(String oidString, String txGuid)
 182  
     {
 183  
         ObjectLocks objectLocks = null;
 184  
         synchronized (locktable)
 185  
         {
 186  
                 objectLocks = (ObjectLocks) locktable.get(oidString);
 187  
         }
 188  
         if (objectLocks == null)
 189  
         {
 190  
             return;
 191  
         }
 192  
         else
 193  
         {
 194  
                 /**
 195  
                  * MBAIRD, last one out, close the door and turn off the lights.
 196  
                  * if no locks (readers or writers) exist for this object, let's remove
 197  
                  * it from the locktable.
 198  
                  */
 199  
                 synchronized (locktable)
 200  
                 {
 201  
                         Map readers = objectLocks.getReaders();
 202  
                         readers.remove(txGuid);
 203  
                     if ((objectLocks.getWriter() == null) && (readers.size() == 0))
 204  
                     {
 205  
                             locktable.remove(oidString);
 206  
                     }
 207  
                 }
 208  
         }
 209  
     }
 210  
 
 211  
         void removeReaderByLock(LockEntry lock)
 212  
         {
 213  
                 String oidString = lock.getOidString();
 214  
                 String txGuid = lock.getTransactionId();
 215  
                 removeReaderInternal(oidString, txGuid);
 216  
         }
 217  
         
 218  
     /**
 219  
      * remove a writer lock entry for transaction tx on object obj
 220  
      * from the persistent storage.
 221  
      */
 222  
     public void removeWriter(LockEntry writer)
 223  
     {
 224  
         checkTimedOutLocks();
 225  
 
 226  
         String oidString = writer.getOidString();
 227  
         ObjectLocks objectLocks = null;
 228  
         synchronized (locktable)
 229  
         {
 230  
                 objectLocks = (ObjectLocks) locktable.get(oidString);
 231  
         }
 232  
         if (objectLocks == null)
 233  
         {
 234  
             return;
 235  
         }
 236  
         else
 237  
         {
 238  
                         /**
 239  
                  * MBAIRD, last one out, close the door and turn off the lights.
 240  
                  * if no locks (readers or writers) exist for this object, let's remove
 241  
                  * it from the locktable.
 242  
                  */
 243  
                 synchronized (locktable)
 244  
                 {
 245  
                         Map readers = objectLocks.getReaders();
 246  
                         objectLocks.setWriter(null);
 247  
                         // no need to check if writer is null, we just set it.
 248  
                     if (readers.size() == 0)
 249  
                     {
 250  
                             locktable.remove(oidString);
 251  
                     }
 252  
                 }
 253  
         }
 254  
     }
 255  
 
 256  
     /**
 257  
      * upgrade a reader lock entry for transaction tx on object obj
 258  
      * and write it to the persistent storage.
 259  
      */
 260  
     public boolean upgradeLock(LockEntry reader)
 261  
     {
 262  
         checkTimedOutLocks();
 263  
 
 264  
         String oidString = reader.getOidString();
 265  
         ObjectLocks objectLocks = null;
 266  
         synchronized (locktable)
 267  
         {
 268  
                 objectLocks = (ObjectLocks) locktable.get(oidString);
 269  
         }
 270  
 
 271  
         if (objectLocks == null)
 272  
         {
 273  
             return false;
 274  
         }
 275  
         else
 276  
         {
 277  
             // add writer entry
 278  
             LockEntry writer = new LockEntry(reader.getOidString(),
 279  
                     reader.getTransactionId(),
 280  
                     System.currentTimeMillis(),
 281  
                     reader.getIsolationLevel(),
 282  
                     LockEntry.LOCK_WRITE);
 283  
             objectLocks.setWriter(writer);
 284  
             // remove reader entry
 285  
             objectLocks.getReaders().remove(reader.getTransactionId());
 286  
             return true;
 287  
         }
 288  
     }
 289  
 
 290  
     /**
 291  
      * generate a writer lock entry for transaction tx on object obj
 292  
      * and write it to the persistent storage.
 293  
      */
 294  
     public boolean setWriter(TransactionImpl tx, Object obj)
 295  
     {
 296  
         checkTimedOutLocks();
 297  
 
 298  
         Identity oid = new Identity(obj, tx.getBroker());
 299  
         LockEntry writer = new LockEntry(oid.toString(),
 300  
                 tx.getGUID(),
 301  
                 System.currentTimeMillis(),
 302  
                 LockStrategyFactory.getIsolationLevel(obj),
 303  
                 LockEntry.LOCK_WRITE);
 304  
         String oidString = oid.toString();
 305  
         setWriterInternal(writer, oidString);
 306  
         return true;
 307  
     }
 308  
 
 309  
     private void setWriterInternal(LockEntry writer, String oidString)
 310  
     {
 311  
         ObjectLocks objectLocks = null;
 312  
         /**
 313  
          * MBAIRD: We need to synchronize the get/put so we don't have two threads
 314  
          * competing to check if something is locked and double-locking it.
 315  
          */
 316  
         synchronized (locktable)
 317  
         {
 318  
             objectLocks = (ObjectLocks) locktable.get(oidString);
 319  
             if (objectLocks == null)
 320  
             {
 321  
                 objectLocks = new ObjectLocks();
 322  
                 locktable.put(oidString, objectLocks);
 323  
             }
 324  
         }
 325  
         objectLocks.setWriter(writer);
 326  
     }
 327  
 
 328  
         void setWriterByLock(LockEntry writer)
 329  
         {
 330  
                 String oidString = writer.getOidString();
 331  
                 setWriterInternal(writer, oidString);
 332  
         }
 333  
 
 334  
     /**
 335  
      * check if there is a reader lock entry for transaction tx on object obj
 336  
      * in the persistent storage.
 337  
      */
 338  
     public boolean hasReadLock(TransactionImpl tx, Object obj)
 339  
     {
 340  
         checkTimedOutLocks();
 341  
 
 342  
         Identity oid = new Identity(obj,getBroker());
 343  
         String oidString = oid.toString();
 344  
                 String txGuid = tx.getGUID();
 345  
         return hasReadLockInternal(oidString, txGuid);
 346  
     }
 347  
 
 348  
     private boolean hasReadLockInternal(String oidString, String txGuid)
 349  
     {
 350  
         ObjectLocks objectLocks = null;
 351  
         synchronized (locktable)
 352  
         {
 353  
                 objectLocks = (ObjectLocks) locktable.get(oidString);
 354  
         }
 355  
         
 356  
         if (objectLocks == null)
 357  
         {
 358  
             return false;
 359  
         }
 360  
         else
 361  
         {
 362  
             
 363  
             LockEntry reader = objectLocks.getReader(txGuid);
 364  
             if (reader != null)
 365  
             {
 366  
                 return true;
 367  
             }
 368  
             else
 369  
             {
 370  
                 return false;
 371  
             }
 372  
         }
 373  
     }
 374  
 
 375  
         boolean hasReadLock(LockEntry entry)
 376  
         {
 377  
                 String oidString = entry.getOidString();
 378  
                 String txGuid = entry.getTransactionId();
 379  
                 return hasReadLockInternal(oidString,txGuid);
 380  
         }
 381  
 
 382  
     private void checkTimedOutLocks()
 383  
     {
 384  
         if (System.currentTimeMillis() - m_lastCleanupAt > CLEANUP_FREQUENCY)
 385  
             {
 386  
                     removeTimedOutLocks(AbstractLockStrategy.DEFAULT_LOCK_TIMEOUT);
 387  
             m_lastCleanupAt = System.currentTimeMillis();
 388  
             }
 389  
     }
 390  
 
 391  
         /**
 392  
         * removes all timed out lock entries from the persistent storage.
 393  
         * The timeout value can be set in the OJB properties file.
 394  
         */
 395  
     private void removeTimedOutLocks(long timeout)
 396  
     {
 397  
         int count = 0;
 398  
         long maxAge = System.currentTimeMillis() - timeout;
 399  
         boolean breakFromLoop = false;
 400  
         ObjectLocks temp = null;
 401  
             synchronized (locktable)
 402  
             {
 403  
                 Iterator it = locktable.values().iterator();
 404  
                 /**
 405  
                  * run this loop while:
 406  
                  * - we have more in the iterator
 407  
                  * - the breakFromLoop flag hasn't been set
 408  
                  * - we haven't removed more than the limit for this cleaning iteration.
 409  
                  */
 410  
                 while (it.hasNext() && !breakFromLoop && (count <= MAX_LOCKS_TO_CLEAN))
 411  
                 {
 412  
                         temp = (ObjectLocks) it.next();
 413  
                         if (temp.getWriter() != null)
 414  
                         {
 415  
                                 if (temp.getWriter().getTimestamp() < maxAge)
 416  
                                 {
 417  
                                         // writer has timed out, set it to null
 418  
                                         temp.setWriter(null);
 419  
                                 }
 420  
                         }
 421  
                         if (temp.getYoungestReader() < maxAge)
 422  
                         {
 423  
                                 // all readers are older than timeout.
 424  
                                 temp.getReaders().clear();
 425  
                                 if (temp.getWriter() == null)
 426  
                                 {
 427  
                                         // all readers and writer are older than timeout,
 428  
                                         // remove the objectLock from the iterator (which
 429  
                                         // is backed by the map, so it will be removed.
 430  
                                         it.remove();
 431  
                                 }
 432  
                         }
 433  
                         else
 434  
                         {
 435  
                                 // we need to walk each reader.
 436  
                                 Iterator readerIt = temp.getReaders().values().iterator();
 437  
                                 LockEntry readerLock = null;
 438  
                                 while (readerIt.hasNext())
 439  
                                 {
 440  
                                         readerLock = (LockEntry) readerIt.next();
 441  
                                         if (readerLock.getTimestamp() < maxAge)
 442  
                                         {
 443  
                                                 // this read lock is old, remove it.
 444  
                                                 readerIt.remove();
 445  
                                         }
 446  
                                 }
 447  
                         }
 448  
                         count++;
 449  
                 }
 450  
             }
 451  
     }
 452  
     
 453  
     /* (non-Javadoc)
 454  
      * @see org.apache.ojb.broker.util.configuration.Configurable#configure(org.apache.ojb.broker.util.configuration.Configuration)
 455  
      */
 456  
     public void configure(Configuration pConfig) throws ConfigurationException
 457  
     {
 458  
         // noop
 459  
 
 460  
     }
 461  
     
 462  
     int getSize()
 463  
     {
 464  
             return locktable.size();
 465  
     }
 466  
 
 467  
 }