KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > plugins > cmp > jdbc2 > schema > TableCache


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.ejb.plugins.cmp.jdbc2.schema;
23
24 import org.jboss.system.ServiceMBeanSupport;
25 import org.jboss.metadata.MetaData;
26 import org.jboss.deployment.DeploymentException;
27 import org.w3c.dom.Element JavaDoc;
28
29 import javax.transaction.Transaction JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.HashMap JavaDoc;
32
33
34 /**
35  * Simple LRU cache. Items are evicted when maxCapacity is exceeded.
36  *
37  * @author <a HREF="mailto:alex@jboss.org">Alexey Loubyansky</a>
38  * @version <tt>$Revision: 37459 $</tt>
39  * @jmx:mbean extends="org.jboss.system.ServiceMBean"
40  */

41 public class TableCache
42    extends ServiceMBeanSupport
43    implements Cache, TableCacheMBean
44 {
45    private Cache.Listener listener = Cache.Listener.NOOP;
46    private final Map JavaDoc rowsById;
47    private CachedRow head;
48    private CachedRow tail;
49    private int maxCapacity;
50    private final int minCapacity;
51
52    private boolean locked;
53
54    private final int partitionIndex;
55
56    public TableCache(int partitionIndex, int initialCapacity, int maxCapacity)
57    {
58       this.maxCapacity = maxCapacity;
59       this.minCapacity = initialCapacity;
60       rowsById = new HashMap JavaDoc(initialCapacity);
61       this.partitionIndex = partitionIndex;
62    }
63
64    public TableCache(Element JavaDoc conf) throws DeploymentException
65    {
66       String JavaDoc str = MetaData.getOptionalChildContent(conf, "min-capacity");
67       minCapacity = (str == null ? 1000 : Integer.parseInt(str));
68       rowsById = new HashMap JavaDoc(minCapacity);
69
70       str = MetaData.getOptionalChildContent(conf, "max-capacity");
71       maxCapacity = (str == null ? 10000 : Integer.parseInt(str));
72
73       this.partitionIndex = 0;
74    }
75
76    /**
77     * @jmx.managed-operation
78     */

79    public void registerListener(Cache.Listener listener)
80    {
81       if(log.isTraceEnabled() && getServiceName() != null)
82       {
83          log.trace("registered listener for " + getServiceName());
84       }
85       this.listener = listener;
86    }
87
88    /**
89     * @jmx.managed-operation
90     */

91    public int size()
92    {
93       lock();
94       try
95       {
96          return rowsById.size();
97       }
98       finally
99       {
100          unlock();
101       }
102    }
103
104    /**
105     * @jmx.managed-attribute
106     */

107    public int getMaxCapacity()
108    {
109       return maxCapacity;
110    }
111
112    /**
113     * @jmx.managed-attribute
114     */

115    public void setMaxCapacity(int maxCapacity)
116    {
117       this.maxCapacity = maxCapacity;
118    }
119
120    /**
121     * @jmx.managed-attribute
122     */

123    public int getMinCapacity()
124    {
125       return minCapacity;
126    }
127
128    public synchronized void lock()
129    {
130       if(locked)
131       {
132          long start = System.currentTimeMillis();
133          while(locked)
134          {
135             try
136             {
137                wait();
138             }
139             catch(InterruptedException JavaDoc e)
140             {
141             }
142          }
143
144          listener.contention(partitionIndex, System.currentTimeMillis() - start);
145       }
146       locked = true;
147    }
148
149    public void lock(Object JavaDoc key)
150    {
151       lock();
152    }
153
154    public synchronized void unlock()
155    {
156       if(!locked)
157       {
158          throw new IllegalStateException JavaDoc("The instance is not locked!");
159       }
160       locked = false;
161       notify();
162    }
163
164    public void unlock(Object JavaDoc key)
165    {
166       unlock();
167    }
168
169    public Object JavaDoc[] getFields(Object JavaDoc pk)
170    {
171       Object JavaDoc[] fields;
172       CachedRow row = (CachedRow) rowsById.get(pk);
173       if(row != null && row.locker == null)
174       {
175          promoteRow(row);
176          fields = new Object JavaDoc[row.fields.length];
177          System.arraycopy(row.fields, 0, fields, 0, fields.length);
178          listener.hit(partitionIndex);
179       }
180       else
181       {
182          fields = null;
183          listener.miss(partitionIndex);
184       }
185       return fields;
186    }
187
188    public Object JavaDoc[] getRelations(Object JavaDoc pk)
189    {
190       Object JavaDoc[] relations;
191       CachedRow row = (CachedRow) rowsById.get(pk);
192       if(row != null && row.relations != null && row.locker == null)
193       {
194          promoteRow(row);
195          relations = new Object JavaDoc[row.relations.length];
196          System.arraycopy(row.relations, 0, relations, 0, relations.length);
197       }
198       else
199       {
200          relations = null;
201       }
202       return relations;
203    }
204
205    public void put(Transaction JavaDoc tx, Object JavaDoc pk, Object JavaDoc[] fields, Object JavaDoc[] relations)
206    {
207       CachedRow row = (CachedRow) rowsById.get(pk);
208       if(row == null) // the row is not cached
209
{
210          Object JavaDoc[] fieldsCopy = new Object JavaDoc[fields.length];
211          System.arraycopy(fields, 0, fieldsCopy, 0, fields.length);
212          row = new CachedRow(pk, fieldsCopy);
213
214          if(relations != null)
215          {
216             Object JavaDoc[] relationsCopy = new Object JavaDoc[relations.length];
217             System.arraycopy(relations, 0, relationsCopy, 0, relations.length);
218             row.relations = relationsCopy;
219          }
220
221          rowsById.put(pk, row);
222
223          if(head == null)
224          {
225             head = row;
226             tail = row;
227          }
228          else
229          {
230             head.prev = row;
231             row.next = head;
232             head = row;
233          }
234       }
235       else if(row.locker == null || row.locker.equals(tx)) // the row is cached
236
{
237          promoteRow(row);
238          System.arraycopy(fields, 0, row.fields, 0, fields.length);
239
240          if(relations != null)
241          {
242             if(row.relations == null)
243             {
244                row.relations = new Object JavaDoc[relations.length];
245             }
246             System.arraycopy(relations, 0, row.relations, 0, relations.length);
247          }
248
249          row.lastUpdated = System.currentTimeMillis();
250          row.locker = null;
251       }
252
253       CachedRow victim = tail;
254       while(rowsById.size() > maxCapacity && victim != null)
255       {
256          CachedRow nextVictim = victim.prev;
257          if(victim.locker == null)
258          {
259             dereference(victim);
260             rowsById.remove(victim.pk);
261             listener.eviction(partitionIndex, row.pk, rowsById.size());
262          }
263          victim = nextVictim;
264       }
265    }
266
267    public void ageOut(long lastUpdated)
268    {
269       CachedRow victim = tail;
270       while(victim != null && victim.lastUpdated < lastUpdated)
271       {
272          CachedRow nextVictim = victim.prev;
273          if(victim.locker == null)
274          {
275             dereference(victim);
276             rowsById.remove(victim.pk);
277             listener.eviction(partitionIndex, victim.pk, rowsById.size());
278          }
279          victim = nextVictim;
280       }
281    }
282
283    public void remove(Transaction JavaDoc tx, Object JavaDoc pk)
284    {
285       CachedRow row = (CachedRow) rowsById.remove(pk);
286       if(row == null || row.locker != null && !tx.equals(row.locker))
287       {
288          String JavaDoc msg = "removal of " +
289             pk +
290             " rejected for " +
291             tx +
292             ": " +
293             (row == null ? "the entry could not be found" : "the entry is locked for update by " + row.locker);
294          throw new RemoveException(msg);
295       }
296
297       dereference(row);
298       row.locker = null;
299    }
300
301    public boolean contains(Transaction JavaDoc tx, Object JavaDoc pk)
302    {
303       CachedRow row = (CachedRow) rowsById.get(pk);
304       return row != null && (row.locker == null || tx.equals(row.locker));
305    }
306
307    public void lockForUpdate(Transaction JavaDoc tx, Object JavaDoc pk) throws Exception JavaDoc
308    {
309       CachedRow row = (CachedRow) rowsById.get(pk);
310       if(row != null)
311       {
312          if(row.locker != null && !tx.equals(row.locker))
313          {
314             throw new Exception JavaDoc("lock acquisition rejected for " +
315                tx +
316                ", the entry is locked for update by " + row.locker + ", id=" + pk);
317          }
318          row.locker = tx;
319       }
320       // else?!
321
}
322
323    public void releaseLock(Transaction JavaDoc tx, Object JavaDoc pk) throws Exception JavaDoc
324    {
325       CachedRow row = (CachedRow) rowsById.get(pk);
326       if(row != null)
327       {
328          if(!tx.equals(row.locker))
329          {
330             throw new Exception JavaDoc("rejected to release lock for " +
331                tx +
332                ", the entry is locked for update by " + row.locker + ", id=" + pk);
333          }
334          row.locker = null;
335       }
336       // else?!
337
}
338
339    public void flush()
340    {
341       this.rowsById.clear();
342       this.head = null;
343       this.tail = null;
344    }
345    
346    public String JavaDoc toString()
347    {
348       StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
349       buf.append('[');
350
351       try
352       {
353          lock();
354
355          CachedRow cursor = head;
356          while(cursor != null)
357          {
358             buf.append('(')
359                .append(cursor.pk)
360                .append('|');
361
362             for(int i = 0; i < cursor.fields.length; ++i)
363             {
364                if(i > 0)
365                {
366                   buf.append(',');
367                }
368
369                buf.append(cursor.fields[i]);
370             }
371
372             buf.append(')');
373
374             cursor = cursor.next;
375          }
376       }
377       finally
378       {
379          unlock();
380       }
381
382       buf.append(']');
383       return buf.toString();
384    }
385
386    // Private
387

388    private void dereference(CachedRow row)
389    {
390       CachedRow next = row.next;
391       CachedRow prev = row.prev;
392
393       if(row == head)
394       {
395          head = next;
396       }
397
398       if(row == tail)
399       {
400          tail = prev;
401       }
402
403       if(next != null)
404       {
405          next.prev = prev;
406       }
407
408       if(prev != null)
409       {
410          prev.next = next;
411       }
412
413       row.next = null;
414       row.prev = null;
415    }
416
417    private void promoteRow(CachedRow row)
418    {
419       if(head == null) // this is the first row in the cache
420
{
421          head = row;
422          tail = row;
423       }
424       else if(row == head) // this is the head
425
{
426       }
427       else if(row == tail) // this is the tail
428
{
429          tail = row.prev;
430          tail.next = null;
431
432          row.prev = null;
433          row.next = head;
434
435          head.prev = row;
436          head = row;
437       }
438       else // somewhere in the middle
439
{
440          CachedRow next = row.next;
441          CachedRow prev = row.prev;
442
443          if(prev != null)
444          {
445             prev.next = next;
446          }
447
448          if(next != null)
449          {
450             next.prev = prev;
451          }
452
453          head.prev = row;
454          row.next = head;
455          row.prev = null;
456          head = row;
457       }
458    }
459
460    private class CachedRow
461    {
462       public final Object JavaDoc pk;
463       public final Object JavaDoc[] fields;
464       public Object JavaDoc[] relations;
465       private Transaction JavaDoc locker;
466
467       private CachedRow next;
468       private CachedRow prev;
469
470       public long lastUpdated = System.currentTimeMillis();
471
472       public CachedRow(Object JavaDoc pk, Object JavaDoc[] fields)
473       {
474          this.pk = pk;
475          this.fields = fields;
476       }
477    }
478 }
479
Popular Tags