001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins.cmp.jdbc2.schema;
023:
024: import org.jboss.system.ServiceMBeanSupport;
025:
026: import javax.transaction.Transaction;
027:
028: /**
029: * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
030: * @version <tt>$Revision: 60056 $</tt>
031: * @jmx:mbean extends="org.jboss.system.ServiceMBean"
032: */
033: public class PartitionedTableCache extends ServiceMBeanSupport
034: implements Cache, PartitionedTableCacheMBean {
035: private Cache.Listener listener = Cache.Listener.NOOP;
036:
037: private final int minCapacity;
038: private final int minPartitionCapacity;
039: private int maxCapacity;
040: private int maxPartitionCapacity;
041:
042: private final TableCache[] partitions;
043:
044: private Overager overager;
045:
046: public PartitionedTableCache(int minCapacity, int maxCapacity,
047: int partitionsTotal) {
048: this .minCapacity = minCapacity;
049: this .maxCapacity = maxCapacity;
050:
051: minPartitionCapacity = minCapacity / partitionsTotal + 1;
052: maxPartitionCapacity = maxCapacity / partitionsTotal + 1;
053: partitions = new TableCache[partitionsTotal];
054: for (int i = 0; i < partitions.length; ++i) {
055: partitions[i] = new TableCache(i, minPartitionCapacity,
056: maxPartitionCapacity);
057: }
058:
059: if (log.isTraceEnabled()) {
060: log.trace("min-capacity=" + minCapacity + ", max-capacity="
061: + maxCapacity + ", partitions=" + partitionsTotal);
062: }
063: }
064:
065: public void stopService() {
066: if (overager != null) {
067: overager.stop();
068: }
069: }
070:
071: public void initOverager(long period, long maxAge, String threadName) {
072: final long periodMs = period * 1000;
073: final long maxAgeMs = maxAge * 1000;
074: overager = new Overager(maxAgeMs, periodMs);
075: new Thread(overager, threadName).start();
076: }
077:
078: /**
079: * @jmx.managed-operation
080: */
081: public void registerListener(Cache.Listener listener) {
082: if (log.isTraceEnabled()) {
083: log.trace("registered listener for " + getServiceName());
084: }
085:
086: this .listener = listener;
087: for (int i = 0; i < partitions.length; ++i) {
088: partitions[i].registerListener(listener);
089: }
090: }
091:
092: /**
093: * @jmx.managed-operation
094: */
095: public int size() {
096: int size = 0;
097: for (int i = 0; i < partitions.length; ++i) {
098: size += partitions[i].size();
099: }
100: return size;
101: }
102:
103: /**
104: * @jmx.managed-attribute
105: */
106: public int getMaxCapacity() {
107: return maxCapacity;
108: }
109:
110: /**
111: * @jmx.managed-attribute
112: */
113: public void setMaxCapacity(int maxCapacity) {
114: this .maxCapacity = maxCapacity;
115: this .maxPartitionCapacity = maxCapacity / partitions.length + 1;
116: for (int i = 0; i < partitions.length; ++i) {
117: partitions[i].setMaxCapacity(maxPartitionCapacity);
118: }
119: }
120:
121: /**
122: * @jmx.managed-attribute
123: */
124: public int getMinCapacity() {
125: return minCapacity;
126: }
127:
128: /**
129: * @jmx.managed-attribute
130: */
131: public int getPartitionsTotal() {
132: return partitions.length;
133: }
134:
135: /**
136: * @jmx.managed-attribute
137: */
138: public int getMinPartitionCapacity() {
139: return minPartitionCapacity;
140: }
141:
142: /**
143: * @jmx.managed-attribute
144: */
145: public int getMaxPartitionCapacity() {
146: return maxPartitionCapacity;
147: }
148:
149: public void lock() {
150: }
151:
152: public void lock(Object key) {
153: int partitionIndex = getPartitionIndex(key);
154: partitions[partitionIndex].lock(key);
155: }
156:
157: public void unlock() {
158: }
159:
160: public void unlock(Object key) {
161: int partitionIndex = getPartitionIndex(key);
162: partitions[partitionIndex].unlock(key);
163: }
164:
165: public Object[] getFields(Object pk) {
166: final int i = getPartitionIndex(pk);
167: return partitions[i].getFields(pk);
168: }
169:
170: public Object[] getRelations(Object pk) {
171: final int i = getPartitionIndex(pk);
172: return partitions[i].getRelations(pk);
173: }
174:
175: public void put(Transaction tx, Object pk, Object[] fields,
176: Object[] relations) {
177: final int i = getPartitionIndex(pk);
178: partitions[i].put(tx, pk, fields, relations);
179: }
180:
181: public void remove(Transaction tx, Object pk) {
182: final int i = getPartitionIndex(pk);
183: partitions[i].remove(tx, pk);
184: }
185:
186: public boolean contains(Transaction tx, Object pk) {
187: final int i = getPartitionIndex(pk);
188: return partitions[i].contains(tx, pk);
189: }
190:
191: public void lockForUpdate(Transaction tx, Object pk)
192: throws Exception {
193: final int i = getPartitionIndex(pk);
194: partitions[i].lockForUpdate(tx, pk);
195: }
196:
197: public void releaseLock(Transaction tx, Object pk) throws Exception {
198: final int i = getPartitionIndex(pk);
199: partitions[i].releaseLock(tx, pk);
200: }
201:
202: public void flush() {
203: for (int i = 0; i < partitions.length; ++i) {
204: final TableCache partition = partitions[i];
205: partition.lock();
206: try {
207: partition.flush();
208: } finally {
209: partition.unlock();
210: }
211: }
212: }
213:
214: // Private
215:
216: private int getPartitionIndex(Object key) {
217: int hash = key.hashCode();
218: // make it positive
219: if (hash < 0) {
220: hash = hash == Integer.MIN_VALUE ? Integer.MAX_VALUE
221: : -hash;
222: }
223: return hash % partitions.length;
224: }
225:
226: // Inner
227:
228: private class Overager implements Runnable {
229: private final long maxAgeMs;
230: private final long periodMs;
231: private boolean run = true;
232:
233: public Overager(long maxAgeMs, long periodMs) {
234: this .maxAgeMs = maxAgeMs;
235: this .periodMs = periodMs;
236: }
237:
238: public void stop() {
239: run = false;
240: }
241:
242: public void run() {
243: while (run) {
244: long lastUpdated = System.currentTimeMillis()
245: - maxAgeMs;
246: for (int i = 0; i < partitions.length; ++i) {
247: partitions[i].ageOut(lastUpdated);
248: }
249:
250: try {
251: Thread.sleep(periodMs);
252: } catch (InterruptedException e) {
253: }
254: }
255: }
256: }
257: }
|