001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.store;
018:
019: import org.apache.avalon.framework.activity.Startable;
020: import org.apache.avalon.framework.configuration.Configurable;
021: import org.apache.avalon.framework.configuration.Configuration;
022: import org.apache.avalon.framework.configuration.ConfigurationException;
023: import org.apache.avalon.framework.logger.AbstractLogEnabled;
024: import org.apache.avalon.framework.parameters.Parameters;
025: import org.apache.avalon.framework.thread.ThreadSafe;
026:
027: import java.util.ArrayList;
028: import java.util.Iterator;
029:
030: /**
031: * This class is a implentation of a StoreJanitor. Store classes
032: * can register to the StoreJanitor. When memory is too low,
033: * the StoreJanitor frees the registered caches until memory is normal.
034: *
035: * @deprecated Use the Avalon Excalibur Store instead.
036: *
037: * @author <a href="mailto:cs@ffzj0ia9.bank.dresdner.net">Christian Schmitt</a>
038: * @author <a href="mailto:g-froehlich@gmx.de">Gerhard Froehlich</a>
039: * @author <a href="mailto:proyal@managingpartners.com">Peter Royal</a>
040: * @version CVS $Id: StoreJanitorImpl.java 433543 2006-08-22 06:22:54Z crossley $
041: */
042: public class StoreJanitorImpl extends AbstractLogEnabled implements
043: StoreJanitor, Configurable, ThreadSafe, Runnable, Startable {
044:
045: private int freememory = -1;
046: private int heapsize = -1;
047: private int cleanupthreadinterval = -1;
048: private int priority = -1;
049: private Runtime jvm;
050: private ArrayList storelist;
051: private int index = -1;
052: private static boolean doRun = false;
053: private double fraction;
054:
055: /**
056: * Initialize the StoreJanitorImpl.
057: * A few options can be used :
058: * <UL>
059: * <LI>freememory = how many bytes shall be always free in the jvm</LI>
060: * <LI>heapsize = max. size of jvm memory consumption</LI>
061: * <LI>cleanupthreadinterval = how often (sec) shall run the cleanup thread</LI>
062: * <LI>threadpriority = priority of the thread (1-10). (Default: 10)</LI>
063: * </UL>
064: *
065: * @param conf the Configuration of the application
066: * @exception ConfigurationException
067: */
068: public void configure(Configuration conf)
069: throws ConfigurationException {
070: if (this .getLogger().isDebugEnabled()) {
071: this .getLogger().debug("Configure StoreJanitorImpl");
072: }
073: this .setJVM(Runtime.getRuntime());
074:
075: Parameters params = Parameters.fromConfiguration(conf);
076: this .setFreememory(params.getParameterAsInteger("freememory",
077: 1000000));
078: this .setHeapsize(params.getParameterAsInteger("heapsize",
079: 60000000));
080: this .setCleanupthreadinterval(params.getParameterAsInteger(
081: "cleanupthreadinterval", 10));
082: this .setPriority(params.getParameterAsInteger("threadpriority",
083: Thread.currentThread().getPriority()));
084: int percent = params.getParameterAsInteger("percent_to_free",
085: 10);
086:
087: if ((this .getFreememory() < 1)) {
088: throw new ConfigurationException(
089: "StoreJanitorImpl freememory parameter has to be greater then 1");
090: }
091: if ((this .getHeapsize() < 1)) {
092: throw new ConfigurationException(
093: "StoreJanitorImpl heapsize parameter has to be greater then 1");
094: }
095: if ((this .getCleanupthreadinterval() < 1)) {
096: throw new ConfigurationException(
097: "StoreJanitorImpl cleanupthreadinterval parameter has to be greater then 1");
098: }
099: if ((this .getPriority() < 1)) {
100: throw new ConfigurationException(
101: "StoreJanitorImpl threadpriority has to be greater then 1");
102: }
103: if ((percent > 100 && percent < 1)) {
104: throw new ConfigurationException(
105: "StoreJanitorImpl percent_to_free, has to be between 1 and 100");
106: }
107:
108: this .fraction = percent / 100.0;
109: this .setStoreList(new ArrayList());
110: }
111:
112: public void start() {
113: doRun = true;
114: Thread checker = new Thread(this );
115: if (this .getLogger().isDebugEnabled()) {
116: this .getLogger().debug("Intializing checker thread");
117: }
118: checker.setPriority(this .getPriority());
119: checker.setDaemon(true);
120: checker.setName("checker");
121: checker.start();
122: }
123:
124: public void stop() {
125: doRun = false;
126: }
127:
128: /**
129: * The "checker" thread checks if memory is running low in the jvm.
130: */
131: public void run() {
132: while (doRun) {
133: // amount of memory used is greater then heapsize
134: if (this .memoryLow()) {
135: if (this .getLogger().isDebugEnabled()) {
136: this .getLogger().debug(
137: "Invoking garbage collection, total memory = "
138: + this .getJVM().totalMemory()
139: + ", free memory = "
140: + this .getJVM().freeMemory());
141: }
142:
143: //this.freePhysicalMemory();
144:
145: if (this .getLogger().isDebugEnabled()) {
146: this .getLogger().debug(
147: "Garbage collection complete, total memory = "
148: + this .getJVM().totalMemory()
149: + ", free memory = "
150: + this .getJVM().freeMemory());
151: }
152:
153: synchronized (this ) {
154: if (this .memoryLow()
155: && this .getStoreList().size() > 0) {
156: this .freeMemory();
157: this .setIndex(this .getIndex() + 1);
158: }
159: }
160: }
161: try {
162: Thread.sleep(this .cleanupthreadinterval * 1000);
163: } catch (InterruptedException ignore) {
164: }
165: }
166: }
167:
168: /**
169: * Method to check if memory is running low in the JVM.
170: *
171: * @return true if memory is low
172: */
173: private boolean memoryLow() {
174: if (this .getLogger().isDebugEnabled()) {
175: this .getLogger().debug(
176: "JVM total Memory: " + this .getJVM().totalMemory());
177: this .getLogger().debug(
178: "JVM free Memory: " + this .getJVM().freeMemory());
179: }
180:
181: if ((this .getJVM().totalMemory() >= this .getHeapsize())
182: && (this .getJVM().freeMemory() < this .getFreememory())) {
183: if (this .getLogger().isDebugEnabled()) {
184: this .getLogger().debug("Memory is low = true");
185: }
186: return true;
187: } else {
188: if (this .getLogger().isDebugEnabled()) {
189: this .getLogger().debug("Memory is low = false");
190: }
191: return false;
192: }
193: }
194:
195: /**
196: * This method register the stores
197: *
198: * @param store the store to be registered
199: */
200: public void register(Store store) {
201: this .getStoreList().add(store);
202: if (this .getLogger().isDebugEnabled()) {
203: this .getLogger().debug("Registering store instance");
204: this .getLogger().debug(
205: "Size of StoreJanitor now:"
206: + this .getStoreList().size());
207: }
208: }
209:
210: /**
211: * This method unregister the stores
212: *
213: * @param store the store to be unregistered
214: */
215: public void unregister(Store store) {
216: this .getStoreList().remove(store);
217: if (this .getLogger().isDebugEnabled()) {
218: this .getLogger().debug("Unregister store instance");
219: this .getLogger().debug(
220: "Size of StoreJanitor now:"
221: + this .getStoreList().size());
222: }
223: }
224:
225: /**
226: * This method return a java.util.Iterator of every registered stores
227: *
228: * <i>The iterators returned is fail-fast: if list is structurally
229: * modified at any time after the iterator is created, in any way, the
230: * iterator will throw a ConcurrentModificationException. Thus, in the
231: * face of concurrent modification, the iterator fails quickly and
232: * cleanly, rather than risking arbitrary, non-deterministic behavior at
233: * an undetermined time in the future.</i>
234: *
235: * @return a java.util.Iterator
236: */
237: public Iterator iterator() {
238: return this .getStoreList().iterator();
239: }
240:
241: /**
242: * Round Robin alghorithm for freeing the registerd caches.
243: */
244: private void freeMemory() {
245: Store store;
246:
247: try {
248: //Determine elements in Store:
249: if (this .getLogger().isDebugEnabled()) {
250: this .getLogger().debug(
251: "StoreList size=" + this .getStoreList().size());
252: this .getLogger().debug(
253: "Actual Index position: " + this .getIndex());
254: }
255: if (this .getIndex() < this .getStoreList().size()) {
256: if (this .getIndex() == -1) {
257: this .setIndex(0);
258: store = (Store) this .getStoreList().get(
259: this .getIndex());
260:
261: if (this .getLogger().isDebugEnabled()) {
262: this .getLogger().debug(
263: "Freeing Store: " + this .getIndex());
264: }
265:
266: //delete proportionate elements out of the cache as
267: //configured.
268: int limit = this .calcToFree(store);
269: for (int i = 0; i < limit; i++) {
270: store.free();
271: }
272: } else {
273: store = (Store) this .getStoreList().get(
274: this .getIndex());
275:
276: if (this .getLogger().isDebugEnabled()) {
277: this .getLogger().debug(
278: "Freeing Store: " + this .getIndex());
279: }
280:
281: //delete proportionate elements out of the cache as
282: //configured.
283: int limit = this .calcToFree(store);
284: for (int i = 0; i < limit; i++) {
285: store.free();
286: }
287: }
288: } else {
289: if (this .getLogger().isDebugEnabled()) {
290: this .getLogger().debug(
291: "Starting from the beginning");
292: }
293:
294: this .resetIndex();
295: this .setIndex(0);
296: store = (Store) this .getStoreList()
297: .get(this .getIndex());
298:
299: //delete proportionate elements out of the cache as
300: //configured.
301: int limit = this .calcToFree(store);
302: for (int i = 0; i < limit; i++) {
303: store.free();
304: }
305: }
306: } catch (Exception e) {
307: this .getLogger().error("Error in freeMemory()", e);
308: }
309: }
310:
311: /**
312: * This method calculates the number of Elements to be freememory
313: * out of the Cache.
314: *
315: * @param store the Store which was selected as victim
316: * @return number of elements to be removed!
317: */
318: private int calcToFree(Store store) {
319: int cnt = store.size();
320: if (cnt < 0) {
321: if (getLogger().isDebugEnabled()) {
322: getLogger()
323: .debug("Unknown size of the store: " + store);
324: }
325: return 0;
326: }
327: return (int) (cnt * fraction);
328: }
329:
330: /**
331: * This method forces the garbage collector
332: private void freePhysicalMemory() {
333: this.getJVM().runFinalization();
334: this.getJVM().gc();
335: }
336: */
337:
338: private int getFreememory() {
339: return freememory;
340: }
341:
342: private void setFreememory(int _freememory) {
343: this .freememory = _freememory;
344: }
345:
346: private int getHeapsize() {
347: return this .heapsize;
348: }
349:
350: private void setHeapsize(int _heapsize) {
351: this .heapsize = _heapsize;
352: }
353:
354: private int getCleanupthreadinterval() {
355: return this .cleanupthreadinterval;
356: }
357:
358: private void setCleanupthreadinterval(int _cleanupthreadinterval) {
359: this .cleanupthreadinterval = _cleanupthreadinterval;
360: }
361:
362: private int getPriority() {
363: return this .priority;
364: }
365:
366: private void setPriority(int _priority) {
367: this .priority = _priority;
368: }
369:
370: private Runtime getJVM() {
371: return this .jvm;
372: }
373:
374: private void setJVM(Runtime _runtime) {
375: this .jvm = _runtime;
376: }
377:
378: private ArrayList getStoreList() {
379: return this .storelist;
380: }
381:
382: private void setStoreList(ArrayList _storelist) {
383: this .storelist = _storelist;
384: }
385:
386: private void setIndex(int _index) {
387: if (this .getLogger().isDebugEnabled()) {
388: this .getLogger().debug("Setting index=" + _index);
389: }
390: this .index = _index;
391: }
392:
393: private int getIndex() {
394: return this .index;
395: }
396:
397: private void resetIndex() {
398: if (this .getLogger().isDebugEnabled()) {
399: this .getLogger().debug("Reseting index");
400: }
401: this .index = -1;
402: }
403: }
|