extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/ReservedItems.java

Code
Comments
Other
Rev Date Author Line
2614 29 Aug 14 nicklas 1 package net.sf.basedb.reggie;
2614 29 Aug 14 nicklas 2
2614 29 Aug 14 nicklas 3 import java.util.HashMap;
2614 29 Aug 14 nicklas 4 import java.util.Iterator;
2614 29 Aug 14 nicklas 5 import java.util.Map;
2614 29 Aug 14 nicklas 6
4881 03 Jul 18 nicklas 7 import net.sf.basedb.core.DbControl;
4881 03 Jul 18 nicklas 8 import net.sf.basedb.core.TransactionalAction;
4881 03 Jul 18 nicklas 9
2614 29 Aug 14 nicklas 10 /**
4306 17 Jan 17 nicklas 11   Simple implementation for keeping track of reserved "items" for a dedicated 
2614 29 Aug 14 nicklas 12   amount of time. After the given timeout expires the items are considered
2614 29 Aug 14 nicklas 13   removed.
2614 29 Aug 14 nicklas 14   
2614 29 Aug 14 nicklas 15   @since 2.16
2614 29 Aug 14 nicklas 16 */
2614 29 Aug 14 nicklas 17 public class ReservedItems<I> 
2614 29 Aug 14 nicklas 18 {
2614 29 Aug 14 nicklas 19
4889 06 Jul 18 nicklas 20   private final Map<I, Reservation> reserve;
2614 29 Aug 14 nicklas 21   private final long timeout;
2614 29 Aug 14 nicklas 22   private long nextCleanup;
2614 29 Aug 14 nicklas 23   
2614 29 Aug 14 nicklas 24   /**
2614 29 Aug 14 nicklas 25     Create a new reserved cache with the given timeout.
2614 29 Aug 14 nicklas 26     @param timeout Timeout in seconds
2614 29 Aug 14 nicklas 27   */
2614 29 Aug 14 nicklas 28   public ReservedItems(int timeout)
2614 29 Aug 14 nicklas 29   {
2614 29 Aug 14 nicklas 30     this.timeout = timeout * 1000;
2614 29 Aug 14 nicklas 31     this.nextCleanup = System.currentTimeMillis() + 10 * this.timeout;
4889 06 Jul 18 nicklas 32     this.reserve = new HashMap<I, Reservation>();
2614 29 Aug 14 nicklas 33   }
2614 29 Aug 14 nicklas 34
2614 29 Aug 14 nicklas 35   /**
2614 29 Aug 14 nicklas 36     Try to reserve the given item. If it is not currently
2614 29 Aug 14 nicklas 37     reserved, a new reservation is made and TRUE is returned.
2614 29 Aug 14 nicklas 38     If the item is already reserved, FALSE is returned.
4889 06 Jul 18 nicklas 39     The DbControl parameter is optional, but if it is used
4889 06 Jul 18 nicklas 40     the reservation can be replaced before the timeout if
4889 06 Jul 18 nicklas 41     this method is called again with a different DbControl
4889 06 Jul 18 nicklas 42     created from the same session.
4889 06 Jul 18 nicklas 43     @since 4.19
2614 29 Aug 14 nicklas 44   */
4889 06 Jul 18 nicklas 45   public synchronized boolean reserve(DbControl dc, I item)
2614 29 Aug 14 nicklas 46   {
2614 29 Aug 14 nicklas 47     long now = System.currentTimeMillis();
2614 29 Aug 14 nicklas 48     
4889 06 Jul 18 nicklas 49     Reservation newReservation = new Reservation(now + timeout, dc);
4889 06 Jul 18 nicklas 50     Reservation existingReservation = reserve.get(item);
4889 06 Jul 18 nicklas 51     if (existingReservation != null && existingReservation.timeout > now) 
2614 29 Aug 14 nicklas 52     {
2614 29 Aug 14 nicklas 53       // The item is reserved and has not timed out
4889 06 Jul 18 nicklas 54       // If it the SAME session, but DIFFERENT transaction
4889 06 Jul 18 nicklas 55       // we allow the reservation to override
4889 06 Jul 18 nicklas 56       if (!existingReservation.canBeReplacedWith(newReservation))
4889 06 Jul 18 nicklas 57       {
4889 06 Jul 18 nicklas 58         return false;
4889 06 Jul 18 nicklas 59       }
2614 29 Aug 14 nicklas 60     }
2614 29 Aug 14 nicklas 61     
2614 29 Aug 14 nicklas 62     // Reserve the given item
4889 06 Jul 18 nicklas 63     reserve.put(item, newReservation);
2614 29 Aug 14 nicklas 64     
2614 29 Aug 14 nicklas 65     // Do cleanup if it was a long time ago
2614 29 Aug 14 nicklas 66     if (now > nextCleanup) cleanUp();
2614 29 Aug 14 nicklas 67     
2614 29 Aug 14 nicklas 68     return true;
2614 29 Aug 14 nicklas 69   }
2614 29 Aug 14 nicklas 70   
4881 03 Jul 18 nicklas 71   /**
4881 03 Jul 18 nicklas 72     Release the reserved item. 
4881 03 Jul 18 nicklas 73     @since 4.19
4881 03 Jul 18 nicklas 74   */
4881 03 Jul 18 nicklas 75   public synchronized void release(I item)
4881 03 Jul 18 nicklas 76   {
4881 03 Jul 18 nicklas 77     reserve.remove(item);
4881 03 Jul 18 nicklas 78   }
4881 03 Jul 18 nicklas 79   
4881 03 Jul 18 nicklas 80   /**
4881 03 Jul 18 nicklas 81     Releases the reserved item if the transaction is rolled back.
4881 03 Jul 18 nicklas 82     @since 4.19
4881 03 Jul 18 nicklas 83   */
4881 03 Jul 18 nicklas 84   public void releaseIfTransactionFails(DbControl dc, I item)
4881 03 Jul 18 nicklas 85   {
4881 03 Jul 18 nicklas 86     dc.addTransactionalAction(new ReleaseAtEndOfTransaction(item, false, true));
4881 03 Jul 18 nicklas 87   }
4881 03 Jul 18 nicklas 88
4881 03 Jul 18 nicklas 89   /**
4881 03 Jul 18 nicklas 90     Releases the reserved item when the transactions ended (no matter if it
4881 03 Jul 18 nicklas 91     is committed or rolled back).
4881 03 Jul 18 nicklas 92     @since 4.19
4881 03 Jul 18 nicklas 93   */
4881 03 Jul 18 nicklas 94   public void releaseAtEndOfTransaction(DbControl dc, I item)
4881 03 Jul 18 nicklas 95   {
4881 03 Jul 18 nicklas 96     dc.addTransactionalAction(new ReleaseAtEndOfTransaction(item, true, true));
4881 03 Jul 18 nicklas 97   }
4881 03 Jul 18 nicklas 98   
2614 29 Aug 14 nicklas 99   private void cleanUp()
2614 29 Aug 14 nicklas 100   {
2614 29 Aug 14 nicklas 101     long now = System.currentTimeMillis();
4889 06 Jul 18 nicklas 102     Iterator<Reservation> it = reserve.values().iterator();
2614 29 Aug 14 nicklas 103     while (it.hasNext())
2614 29 Aug 14 nicklas 104     {
4889 06 Jul 18 nicklas 105       if (it.next().timeout < now) it.remove();
2614 29 Aug 14 nicklas 106     }
2614 29 Aug 14 nicklas 107     nextCleanup = now + 10 * timeout;
2614 29 Aug 14 nicklas 108   }
2614 29 Aug 14 nicklas 109   
4889 06 Jul 18 nicklas 110   public void debug()
4889 06 Jul 18 nicklas 111   {
4889 06 Jul 18 nicklas 112     System.out.println("reserved: " + reserve);
4889 06 Jul 18 nicklas 113   }
4889 06 Jul 18 nicklas 114   
4889 06 Jul 18 nicklas 115   static class Reservation
4889 06 Jul 18 nicklas 116   {
4889 06 Jul 18 nicklas 117     final long timeout;
4889 06 Jul 18 nicklas 118     final String sessionId;
4889 06 Jul 18 nicklas 119     final int transactionId;
4889 06 Jul 18 nicklas 120     
4889 06 Jul 18 nicklas 121     Reservation(long timeout, DbControl dc)
4889 06 Jul 18 nicklas 122     {
4889 06 Jul 18 nicklas 123       this.timeout = timeout;
4889 06 Jul 18 nicklas 124       this.sessionId = dc != null ? dc.getSessionControl().getId() : null;
4889 06 Jul 18 nicklas 125       this.transactionId = dc != null ? dc.hashCode() : 0;
4889 06 Jul 18 nicklas 126     }
4889 06 Jul 18 nicklas 127     
4889 06 Jul 18 nicklas 128     /**
4889 06 Jul 18 nicklas 129       Check if this reservation can be replaced with the other
4889 06 Jul 18 nicklas 130       reservation. This is allowed if both reservations have
4889 06 Jul 18 nicklas 131       the same session id but different transaction id.
4889 06 Jul 18 nicklas 132     */
4889 06 Jul 18 nicklas 133     boolean canBeReplacedWith(Reservation other)
4889 06 Jul 18 nicklas 134     {
4889 06 Jul 18 nicklas 135       return sessionId != null && sessionId.equals(other.sessionId) && transactionId != other.transactionId;
4889 06 Jul 18 nicklas 136     }
4889 06 Jul 18 nicklas 137     
4889 06 Jul 18 nicklas 138     @Override
4889 06 Jul 18 nicklas 139     public String toString()
4889 06 Jul 18 nicklas 140     {
4889 06 Jul 18 nicklas 141       return timeout + "[" + sessionId + "@" + transactionId + "]";
4889 06 Jul 18 nicklas 142     }
4889 06 Jul 18 nicklas 143   }
4889 06 Jul 18 nicklas 144   
4881 03 Jul 18 nicklas 145   /**
4881 03 Jul 18 nicklas 146     Callback handler for releasing items when a transaction is committed and/or rolled back.
4881 03 Jul 18 nicklas 147   */
4881 03 Jul 18 nicklas 148   class ReleaseAtEndOfTransaction
4881 03 Jul 18 nicklas 149     implements TransactionalAction
4881 03 Jul 18 nicklas 150   {
4881 03 Jul 18 nicklas 151     private final I item;
4881 03 Jul 18 nicklas 152     private final boolean atCommit;
4881 03 Jul 18 nicklas 153     private final boolean atRollback;
4881 03 Jul 18 nicklas 154     
4881 03 Jul 18 nicklas 155     public ReleaseAtEndOfTransaction(I item, boolean atCommit, boolean atRollback) 
4881 03 Jul 18 nicklas 156     {
4881 03 Jul 18 nicklas 157       this.item = item;
4881 03 Jul 18 nicklas 158       this.atCommit = atCommit;
4881 03 Jul 18 nicklas 159       this.atRollback = atRollback;
4881 03 Jul 18 nicklas 160     }
4881 03 Jul 18 nicklas 161     
4881 03 Jul 18 nicklas 162     @Override
4881 03 Jul 18 nicklas 163     public void onBeforeCommit() 
4881 03 Jul 18 nicklas 164     {}
4881 03 Jul 18 nicklas 165     @Override
4881 03 Jul 18 nicklas 166     public void onAfterCommit() 
4881 03 Jul 18 nicklas 167     {
4881 03 Jul 18 nicklas 168       if (atCommit) release(item);
4881 03 Jul 18 nicklas 169     }
4881 03 Jul 18 nicklas 170     @Override
4881 03 Jul 18 nicklas 171     public void onRollback() 
4881 03 Jul 18 nicklas 172     {
4881 03 Jul 18 nicklas 173       if (atRollback) release(item);
4881 03 Jul 18 nicklas 174     }
4881 03 Jul 18 nicklas 175   }
2614 29 Aug 14 nicklas 176 }