src/core/net/sf/basedb/util/units/UnitCache.java

Code
Comments
Other
Rev Date Author Line
4555 02 Oct 08 nicklas 1 /**
4555 02 Oct 08 nicklas 2   $Id$
4555 02 Oct 08 nicklas 3
4555 02 Oct 08 nicklas 4   Copyright (C) 2008 Nicklas Nordborg
4555 02 Oct 08 nicklas 5
4555 02 Oct 08 nicklas 6   This file is part of BASE - BioArray Software Environment.
4555 02 Oct 08 nicklas 7   Available at http://base.thep.lu.se/
4555 02 Oct 08 nicklas 8
4555 02 Oct 08 nicklas 9   BASE is free software; you can redistribute it and/or
4555 02 Oct 08 nicklas 10   modify it under the terms of the GNU General Public License
4555 02 Oct 08 nicklas 11   as published by the Free Software Foundation; either version 3
4555 02 Oct 08 nicklas 12   of the License, or (at your option) any later version.
4555 02 Oct 08 nicklas 13
4555 02 Oct 08 nicklas 14   BASE is distributed in the hope that it will be useful,
4555 02 Oct 08 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
4555 02 Oct 08 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
4555 02 Oct 08 nicklas 17   GNU General Public License for more details.
4555 02 Oct 08 nicklas 18
4555 02 Oct 08 nicklas 19   You should have received a copy of the GNU General Public License
4555 02 Oct 08 nicklas 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
4555 02 Oct 08 nicklas 21 */
4555 02 Oct 08 nicklas 22 package net.sf.basedb.util.units;
4555 02 Oct 08 nicklas 23
4555 02 Oct 08 nicklas 24 import java.util.HashMap;
4555 02 Oct 08 nicklas 25 import java.util.List;
4555 02 Oct 08 nicklas 26 import java.util.Map;
4555 02 Oct 08 nicklas 27
4555 02 Oct 08 nicklas 28 import net.sf.basedb.core.ConnectionClosedException;
4555 02 Oct 08 nicklas 29 import net.sf.basedb.core.DbControl;
4555 02 Oct 08 nicklas 30 import net.sf.basedb.core.ItemQuery;
4555 02 Oct 08 nicklas 31 import net.sf.basedb.core.Quantity;
4555 02 Oct 08 nicklas 32 import net.sf.basedb.core.SystemItems;
4555 02 Oct 08 nicklas 33 import net.sf.basedb.core.Type;
4555 02 Oct 08 nicklas 34 import net.sf.basedb.core.Unit;
4555 02 Oct 08 nicklas 35 import net.sf.basedb.core.query.Expressions;
4555 02 Oct 08 nicklas 36 import net.sf.basedb.core.query.Hql;
4555 02 Oct 08 nicklas 37 import net.sf.basedb.core.query.Restrictions;
4555 02 Oct 08 nicklas 38
4555 02 Oct 08 nicklas 39 /**
4555 02 Oct 08 nicklas 40   Helper class for optimized lookup of units by symbol. 
4555 02 Oct 08 nicklas 41   This class will cache units by their symbol and quantity,
4555 02 Oct 08 nicklas 42   making repeated lookup for the same unit only hit the database 
4555 02 Oct 08 nicklas 43   the first time. 
4555 02 Oct 08 nicklas 44   <p>
4555 02 Oct 08 nicklas 45   Note! The implementation of the cache is not thread-safe. If
4555 02 Oct 08 nicklas 46   multiple threads are going to use the cache at the same time
4555 02 Oct 08 nicklas 47   they must synchronize externally.
4555 02 Oct 08 nicklas 48   
4555 02 Oct 08 nicklas 49   @author Nicklas
4555 02 Oct 08 nicklas 50   @version 2.9
4555 02 Oct 08 nicklas 51   @base.modified $Date$
4555 02 Oct 08 nicklas 52 */
4555 02 Oct 08 nicklas 53 public class UnitCache 
4555 02 Oct 08 nicklas 54 {
4555 02 Oct 08 nicklas 55   private DbControl dc;
4555 02 Oct 08 nicklas 56   private ItemQuery<Unit> query;
4555 02 Oct 08 nicklas 57   private Map<CacheKey, Unit> units;
4555 02 Oct 08 nicklas 58   private boolean autoReload;
4555 02 Oct 08 nicklas 59   
4555 02 Oct 08 nicklas 60   /**
4555 02 Oct 08 nicklas 61     Create a new unit cache. Before it can be used a 
4555 02 Oct 08 nicklas 62     {@link DbControl} must be set by {@link #setDbControl(DbControl)}.    
4555 02 Oct 08 nicklas 63   */
4555 02 Oct 08 nicklas 64   public UnitCache()
4555 02 Oct 08 nicklas 65   {
4555 02 Oct 08 nicklas 66     this(null);
4555 02 Oct 08 nicklas 67   }
4555 02 Oct 08 nicklas 68   
4555 02 Oct 08 nicklas 69   /**
4555 02 Oct 08 nicklas 70     Create a new unit cache which is ready to be used immediately.
4555 02 Oct 08 nicklas 71     @param dc The DbControl to use for database access
4555 02 Oct 08 nicklas 72   */
4555 02 Oct 08 nicklas 73   public UnitCache(DbControl dc)
4555 02 Oct 08 nicklas 74   {
4555 02 Oct 08 nicklas 75     this.dc = dc;
4555 02 Oct 08 nicklas 76     this.units = new HashMap<CacheKey, Unit>();
4555 02 Oct 08 nicklas 77   }
4555 02 Oct 08 nicklas 78   
4555 02 Oct 08 nicklas 79   /**
4555 02 Oct 08 nicklas 80     Set the DbControl to use for database access
4555 02 Oct 08 nicklas 81   */
4555 02 Oct 08 nicklas 82   public void setDbControl(DbControl dc)
4555 02 Oct 08 nicklas 83   {
4555 02 Oct 08 nicklas 84     this.dc = dc;
4555 02 Oct 08 nicklas 85     this.query = null;
4555 02 Oct 08 nicklas 86   }
4555 02 Oct 08 nicklas 87   
4555 02 Oct 08 nicklas 88   /**
4555 02 Oct 08 nicklas 89     Get the DbControl that is currently used for database access.
4555 02 Oct 08 nicklas 90   */
4555 02 Oct 08 nicklas 91   public DbControl getDbControl()
4555 02 Oct 08 nicklas 92   {
4555 02 Oct 08 nicklas 93     return dc;
4555 02 Oct 08 nicklas 94   }
4555 02 Oct 08 nicklas 95
4555 02 Oct 08 nicklas 96   /**
4555 02 Oct 08 nicklas 97     Clear the cache of all units.
4555 02 Oct 08 nicklas 98   */
4555 02 Oct 08 nicklas 99   public void resetCache()
4555 02 Oct 08 nicklas 100   {
4555 02 Oct 08 nicklas 101     units.clear();
4555 02 Oct 08 nicklas 102   }
4555 02 Oct 08 nicklas 103
4555 02 Oct 08 nicklas 104   
4555 02 Oct 08 nicklas 105   /**
4555 02 Oct 08 nicklas 106     Specifies if a cached unit that may have been loaded
4555 02 Oct 08 nicklas 107     by a closed DbControl should be reloaded with the
4555 02 Oct 08 nicklas 108     current DbControl or not. This assumes that the cache is
4555 02 Oct 08 nicklas 109     currently using a connected DbControl. If not, a
4555 02 Oct 08 nicklas 110     {@link ConnectionClosedException} is thrown when trying to
4555 02 Oct 08 nicklas 111     find a unit. The default value for this setting is FALSE.
4555 02 Oct 08 nicklas 112     @param autoReload TRUE to automatically reload the unit with
4555 02 Oct 08 nicklas 113       the current DbControl
4555 02 Oct 08 nicklas 114   */
4555 02 Oct 08 nicklas 115   public void setAutoReload(boolean autoReload)
4555 02 Oct 08 nicklas 116   {
4555 02 Oct 08 nicklas 117     this.autoReload = autoReload;
4555 02 Oct 08 nicklas 118   }
4555 02 Oct 08 nicklas 119   
4555 02 Oct 08 nicklas 120   /**
4555 02 Oct 08 nicklas 121     If units that are no longer connected to a DbControl should
4555 02 Oct 08 nicklas 122     automatcally by reloaded with the current DbControl or not.
4555 02 Oct 08 nicklas 123   */
4555 02 Oct 08 nicklas 124   public boolean isAutoReload()
4555 02 Oct 08 nicklas 125   {
4555 02 Oct 08 nicklas 126     return autoReload;
4555 02 Oct 08 nicklas 127   }
4555 02 Oct 08 nicklas 128   
4555 02 Oct 08 nicklas 129   /**
4555 02 Oct 08 nicklas 130     Finds a unit with a given symbol for a given quantity. 
4555 02 Oct 08 nicklas 131     
4555 02 Oct 08 nicklas 132     @param quantityId The ID of the quantity
4555 02 Oct 08 nicklas 133     @param symbol The symbol of the unit
4555 02 Oct 08 nicklas 134     @return A unit object, or null if no unit is found
4555 02 Oct 08 nicklas 135   */
4555 02 Oct 08 nicklas 136   public Unit findUnit(int quantityId, String symbol)
4555 02 Oct 08 nicklas 137   {
4555 02 Oct 08 nicklas 138     CacheKey key = new CacheKey(quantityId, symbol);
4555 02 Oct 08 nicklas 139     Unit unit = null;
4555 02 Oct 08 nicklas 140     if (units.containsKey(key))
4555 02 Oct 08 nicklas 141     {
4555 02 Oct 08 nicklas 142       unit = units.get(key);
4555 02 Oct 08 nicklas 143       if (autoReload && unit.isDetached())
4555 02 Oct 08 nicklas 144       {
7381 22 May 17 nicklas 145         if (dc == null || dc.isClosed())
4555 02 Oct 08 nicklas 146         {
4555 02 Oct 08 nicklas 147           throw new ConnectionClosedException();
4555 02 Oct 08 nicklas 148         }
4555 02 Oct 08 nicklas 149         unit = Unit.getById(dc, unit.getId());
4555 02 Oct 08 nicklas 150         units.put(key, unit);
4555 02 Oct 08 nicklas 151       }
4555 02 Oct 08 nicklas 152     }
4555 02 Oct 08 nicklas 153     else
4555 02 Oct 08 nicklas 154     {
4555 02 Oct 08 nicklas 155       unit = lookupUnitInDb(key, true);
4555 02 Oct 08 nicklas 156     }
4555 02 Oct 08 nicklas 157     return unit;
4555 02 Oct 08 nicklas 158     
4555 02 Oct 08 nicklas 159   }
4555 02 Oct 08 nicklas 160
4555 02 Oct 08 nicklas 161   /**
4555 02 Oct 08 nicklas 162     Finds a unit with a given symbol for a given quantity. 
4555 02 Oct 08 nicklas 163     
4555 02 Oct 08 nicklas 164     @param quantityId The system ID of the quantity
4555 02 Oct 08 nicklas 165     @param symbol The symbol of the unit
4555 02 Oct 08 nicklas 166     @return A unit object, or null if no unit is found
4555 02 Oct 08 nicklas 167   */
4555 02 Oct 08 nicklas 168   public Unit findUnit(String quantityId, String symbol)
4555 02 Oct 08 nicklas 169   {
4555 02 Oct 08 nicklas 170     return findUnit(SystemItems.getId(quantityId), symbol);
4555 02 Oct 08 nicklas 171   }
4555 02 Oct 08 nicklas 172   /**
4555 02 Oct 08 nicklas 173     Finds a unit with a given symbol for a given quantity. 
4555 02 Oct 08 nicklas 174     
4555 02 Oct 08 nicklas 175     @param quantity The quantity
4555 02 Oct 08 nicklas 176     @param symbol The symbol of the unit
4555 02 Oct 08 nicklas 177     @return A unit object, or null if no unit is found
4555 02 Oct 08 nicklas 178   */
4555 02 Oct 08 nicklas 179   public Unit findUnit(Quantity quantity, String symbol)
4555 02 Oct 08 nicklas 180   {
4555 02 Oct 08 nicklas 181     return findUnit(quantity.getId(), symbol);
4555 02 Oct 08 nicklas 182   }
4555 02 Oct 08 nicklas 183   
4555 02 Oct 08 nicklas 184   /**
4555 02 Oct 08 nicklas 185     Lookup a unit in the database and optionally add it
4555 02 Oct 08 nicklas 186     to the cache.
4555 02 Oct 08 nicklas 187   */
4555 02 Oct 08 nicklas 188   private Unit lookupUnitInDb(CacheKey key, boolean addToCache)
4555 02 Oct 08 nicklas 189   {
7381 22 May 17 nicklas 190     if (dc == null || dc.isClosed()) throw new ConnectionClosedException();
4555 02 Oct 08 nicklas 191     if (query == null)
4555 02 Oct 08 nicklas 192     {
4555 02 Oct 08 nicklas 193       query = Unit.getQuery();
4555 02 Oct 08 nicklas 194       query.join(Hql.innerJoin("symbols", "smb"));
4555 02 Oct 08 nicklas 195       query.restrict(
4555 02 Oct 08 nicklas 196         Restrictions.eq(
4555 02 Oct 08 nicklas 197           Hql.property("quantity"), Expressions.parameter("quantity")
4555 02 Oct 08 nicklas 198         )
4555 02 Oct 08 nicklas 199       );
4555 02 Oct 08 nicklas 200       query.restrict(
4555 02 Oct 08 nicklas 201         Restrictions.eq(
4555 02 Oct 08 nicklas 202           Hql.property("smb", "symbol"), Expressions.parameter("symbol")
4555 02 Oct 08 nicklas 203         )
4555 02 Oct 08 nicklas 204       );
4555 02 Oct 08 nicklas 205     }
4555 02 Oct 08 nicklas 206     query.setParameter("quantity", key.quantityId, Type.INT);
4555 02 Oct 08 nicklas 207     query.setParameter("symbol", key.symbol, Type.STRING);
4555 02 Oct 08 nicklas 208     query.setMaxResults(1);
4555 02 Oct 08 nicklas 209     List<Unit> list = query.list(dc);
4555 02 Oct 08 nicklas 210     Unit unit = list.isEmpty() ? null : list.get(0);
4555 02 Oct 08 nicklas 211     if (addToCache) units.put(key, unit);
4555 02 Oct 08 nicklas 212     return unit;
4555 02 Oct 08 nicklas 213   }
4555 02 Oct 08 nicklas 214   
4555 02 Oct 08 nicklas 215   /**
4555 02 Oct 08 nicklas 216     Units are cached by (quantity,symbol) combination. This
4555 02 Oct 08 nicklas 217     class is used as keys to the cache and overrides the 
4555 02 Oct 08 nicklas 218     equals and hashcode methods to makes sure that 
4555 02 Oct 08 nicklas 219     two keys with the same quantity and symbol are considered 
4555 02 Oct 08 nicklas 220     equal.
4555 02 Oct 08 nicklas 221     <p>
4555 02 Oct 08 nicklas 222     This class also implements the comparable interface so that 
4555 02 Oct 08 nicklas 223     it is possible to sort units in the order of their symbols.
4555 02 Oct 08 nicklas 224     If two symbols are equal the one with the lowes quantity id
4555 02 Oct 08 nicklas 225     is sorted first.
4555 02 Oct 08 nicklas 226   */
4555 02 Oct 08 nicklas 227   static class CacheKey
4555 02 Oct 08 nicklas 228     implements Comparable<CacheKey>
4555 02 Oct 08 nicklas 229   {
4555 02 Oct 08 nicklas 230     private final int quantityId;
4555 02 Oct 08 nicklas 231     private final String symbol;
4555 02 Oct 08 nicklas 232
4555 02 Oct 08 nicklas 233     CacheKey(int quantityId, String symbol)
4555 02 Oct 08 nicklas 234     {
4555 02 Oct 08 nicklas 235       this.quantityId = quantityId;
4555 02 Oct 08 nicklas 236       this.symbol = symbol;
4555 02 Oct 08 nicklas 237     }
4555 02 Oct 08 nicklas 238     
4555 02 Oct 08 nicklas 239     /**
4555 02 Oct 08 nicklas 240       From the Object class
4555 02 Oct 08 nicklas 241       ---------------------
4555 02 Oct 08 nicklas 242     */
4555 02 Oct 08 nicklas 243     @Override
4555 02 Oct 08 nicklas 244     public int hashCode()
4555 02 Oct 08 nicklas 245     {
4555 02 Oct 08 nicklas 246       return symbol.hashCode();
4555 02 Oct 08 nicklas 247     }
4555 02 Oct 08 nicklas 248     
4555 02 Oct 08 nicklas 249     @Override
4555 02 Oct 08 nicklas 250     public boolean equals(Object other)
4555 02 Oct 08 nicklas 251     {
4555 02 Oct 08 nicklas 252       if (this == other) return true;
4555 02 Oct 08 nicklas 253       if (other == null || other.getClass() != this.getClass()) return false;
4555 02 Oct 08 nicklas 254       CacheKey ck = (CacheKey)other;
4555 02 Oct 08 nicklas 255       return ck.quantityId == this.quantityId && ck.symbol.equals(this.symbol);
4555 02 Oct 08 nicklas 256     }
4555 02 Oct 08 nicklas 257     // -----------------------------
4555 02 Oct 08 nicklas 258     
4555 02 Oct 08 nicklas 259     /*
4555 02 Oct 08 nicklas 260       From the Comparable interface
4555 02 Oct 08 nicklas 261       -----------------------------
4555 02 Oct 08 nicklas 262     */
4555 02 Oct 08 nicklas 263     @Override
4555 02 Oct 08 nicklas 264     public int compareTo(CacheKey o) 
4555 02 Oct 08 nicklas 265     {
4555 02 Oct 08 nicklas 266       int result = this.symbol.compareTo(o.symbol);
4555 02 Oct 08 nicklas 267       if (result == 0)
4555 02 Oct 08 nicklas 268       {
4555 02 Oct 08 nicklas 269         result = o.quantityId - this.quantityId;
4555 02 Oct 08 nicklas 270       }
4555 02 Oct 08 nicklas 271       return result;
4555 02 Oct 08 nicklas 272     }
4555 02 Oct 08 nicklas 273     // ------------------------------
4555 02 Oct 08 nicklas 274
4555 02 Oct 08 nicklas 275   }
4555 02 Oct 08 nicklas 276   
4555 02 Oct 08 nicklas 277 }