Package org.apache.cocoon.storejanitor

Source Code of org.apache.cocoon.storejanitor.StoreJanitorImpl

/*

============================================================================
                   The Apache Software License, Version 1.1
============================================================================

Copyright (C) 1999-2002 The Apache Software Foundation. All rights reserved.

Redistribution and use in source and binary forms, with or without modifica-
tion, are permitted provided that the following conditions are met:

1. Redistributions of  source code must  retain the above copyright  notice,
    this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any, must
    include  the following  acknowledgment:  "This product includes  software
    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
    Alternately, this  acknowledgment may  appear in the software itself,  if
    and wherever such third-party acknowledgments normally appear.

4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
    used to  endorse or promote  products derived from  this software without
    prior written permission. For written permission, please contact
    apache@apache.org.

5. Products  derived from this software may not  be called "Apache", nor may
    "Apache" appear  in their name,  without prior written permission  of the
    Apache Software Foundation.

THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
(INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software  consists of voluntary contributions made  by many individuals
on  behalf of the Apache Software  Foundation and was  originally created by
Stefano Mazzocchi  <stefano@apache.org>. For more  information on the Apache
Software Foundation, please see <http://www.apache.org/>.

*/
package org.apache.cocoon.storejanitor;

import org.apache.avalon.framework.activity.Startable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.logger.AbstractLoggable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.thread.ThreadSafe;

import org.apache.cocoon.components.store.StoreJanitor;
import org.apache.cocoon.components.store.Store;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;

/**
* This class is a implentation of a StoreJanitor. Store classes
* can register to the StoreJanitor. When memory is too low,
* the StoreJanitor frees the registered caches until memory is normal.
*
*@author     <a href="mailto:cs@ffzj0ia9.bank.dresdner.net">Christian Schmitt</a>
*@author     <a href="mailto:g-froehlich@gmx.de">Gerhard Froehlich</a>
*@author     <a href="mailto:proyal@managingpartners.com">Peter Royal</a>
*@author     <a href="mailto:pdh@totalise.co.uk">Peter Hargreaves</a>
*
*@version CVS $Id: StoreJanitorImpl.java,v 1.2 2002/02/22 06:58:03 cziegeler Exp $
*/
public class StoreJanitorImpl
         extends AbstractLoggable
         implements StoreJanitor,
                    Configurable,
                    ThreadSafe,
                    Runnable,
                    Startable {
    private static boolean doRun = false;

    private int freememory = -1;
    private int heapsize = -1;
    private int cleanupthreadinterval = -1;
    private int priority = -1;
    private Runtime jvm;
    private ArrayList storelist;
    private int index = -1;
    private int m_percent;

    private long inUseAfter;// Remember while sleeping.
    private long sleepPeriod;// Remember while sleeping.
    private long maxRateOfChange;

    /**
     * Initialize the StoreJanitorImpl.
     * A few options can be used :
     * <UL>
     <LI>freememory = how many bytes shall be always free in the jvm</LI>
     <LI>heapsize = max. size of jvm memory consumption</LI>
     <LI>cleanupthreadinterval = how often (sec) shall run the cleanup thread</LI>
     <LI>threadpriority = priority of the thread (1-10). (Default: 10)</LI>
     * </UL>
     *
     *@param  conf                        Description of Parameter
     *@exception  ConfigurationException
     *@since
     */
    public void configure(Configuration conf) throws ConfigurationException {
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("Configure StoreJanitorImpl");
        }
        this.setJVM(Runtime.getRuntime());

        Parameters params = Parameters.fromConfiguration(conf);
        this.setFreememory(params.getParameterAsInteger("freememory", 1000000));
        this.setHeapsize(params.getParameterAsInteger("heapsize", 60000000));
        this.setCleanupthreadinterval(params.getParameterAsInteger("cleanupthreadinterval", 10));
        this.setPriority(params.getParameterAsInteger("threadpriority",
                Thread.currentThread().getPriority()));
        this.m_percent = params.getParameterAsInteger("percent_to_free", 10);

        if ((this.getFreememory() < 1)) {
            throw new ConfigurationException("StoreJanitorImpl freememory parameter has to be greater then 1");
        }
        if ((this.getHeapsize() < 1)) {
            throw new ConfigurationException("StoreJanitorImpl heapsize parameter has to be greater then 1");
        }
        if ((this.getCleanupthreadinterval() < 1)) {
            throw new ConfigurationException("StoreJanitorImpl cleanupthreadinterval parameter has to be greater then 1");
        }
        if ((this.getPriority() < 1)) {
            throw new ConfigurationException("StoreJanitorImpl threadpriority has to be greater then 1");
        }
        if ((this.m_percent > 100 && this.m_percent < 1)) {
            throw new ConfigurationException("StoreJanitorImpl percent_to_free, has to be between 1 and 100");
        }

        this.setStoreList(new ArrayList());
    }

    /**
     *Description of the Method
     *
     *@since
     */
    public void start() {
        doRun = true;
        Thread checker = new Thread(this);
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("Intializing checker thread");
        }
        checker.setPriority(this.getPriority());
        checker.setDaemon(true);
        checker.setName("checker");
        checker.start();
    }

    /**
     *Description of the Method
     *
     *@since
     */
    public void stop() {
        doRun = false;
    }// Remember while sleeping.

    /**
     * The "checker" thread checks if items should be removed from the stores.
     *
     *@since
     */
    public void run() {
        // Initialise for a safe first calculation of rate of change.
        inUseAfter = inUseNow();
        sleepPeriod = Long.MAX_VALUE;
        maxRateOfChange = 1;
        while (doRun) {
            // Monitor the rate of change of heap in use.
            long changeWhileSleeping = inUseNow() - inUseAfter;
            long rateOfChange = longDiv(changeWhileSleeping, sleepPeriod);// bpms (same as or kbps).
            if (maxRateOfChange < rateOfChange) {
                maxRateOfChange = (maxRateOfChange + rateOfChange) / 2;
            }
            // Output debug info.
            debug("Waking after " + sleepPeriod + "ms, in use change " + changeWhileSleeping + " to " + inUseNow() + ", rate " + rateOfChange + "kb/sec, max rate " + maxRateOfChange + "kb/sec");
            debug("maxHeap=" + getMaxHeap() + ", totalHeap=" + jvm.totalMemory() + ", heapIsBig=" + heapIsBig());
            debug("minFree=" + getMinFree() + ",  freeHeap=" + jvm.freeMemory() + ", freeIsLow=" + freeIsLow());
            // If the heap is big, and the free memory is low.
            if (heapIsBig() && freeIsLow()) {
                synchronized (this) {
                    attemptToFreeStorage();
                }
            }
            // Remember memory in use before sleeping in order to calc slope when waking.
            inUseAfter = inUseNow();
            // If time to half fill could be less than max sleep, then sleep for half min time to fill (& remember it to calc slope next time).
            sleepPeriod = minTimeToFill() / 2 < getMaxSleep() ? minTimeToFill() / 2 : getMaxSleep();
            debug("Store checker going to sleep for " + sleepPeriod + "ms, (max sleep=" + getMaxSleep() + "ms), with memory in use=" + inUseAfter);
            try {
                Thread.currentThread().sleep(sleepPeriod);
            } catch (InterruptedException ignore) {
            }
        }
    }

    /**
     * This method register the stores
     *
     *@param  store  Description of Parameter
     *@since
     */
    public void register(Store store) {
        this.getStoreList().add(store);
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("Registering store instance");
            this.getLogger().debug("Size of StoreJanitor now:"
                     + this.getStoreList().size());
        }
    }

    /**
     * This method unregister the stores
     *
     *@param  store  Description of Parameter
     *@since
     */
    public void unregister(Store store) {
        this.getStoreList().remove(store);
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("Unregister store instance");
            this.getLogger().debug("Size of StoreJanitor now:"
                     + this.getStoreList().size());
        }
    }

    /**
     * This method return a java.util.Iterator of every registered stores
     *
     * <i>The iterators returned is fail-fast: if list is structurally
     * modified at any time after the iterator is created, in any way, the
     * iterator will throw a ConcurrentModificationException.  Thus, in the
     * face of concurrent modification, the iterator fails quickly and
     * cleanly, rather than risking arbitrary, non-deterministic behavior at
     * an undetermined time in the future.</i>
     *
     *@return    a java.util.Iterator
     *@since
     */
    public Iterator iterator() {
        return this.getStoreList().iterator();
    }

    /**
     *Sets the freememory attribute of the StoreJanitorImpl object
     *
     *@param  _freememory  The new freememory value
     *@since
     */
    private void setFreememory(int _freememory) {
        this.freememory = _freememory;
    }

    /**
     *Sets the heapsize attribute of the StoreJanitorImpl object
     *
     *@param  _heapsize  The new heapsize value
     *@since
     */
    private void setHeapsize(int _heapsize) {
        this.heapsize = _heapsize;
    }

    /**
     *Sets the cleanupthreadinterval attribute of the StoreJanitorImpl object
     *
     *@param  _cleanupthreadinterval  The new cleanupthreadinterval value
     *@since
     */
    private void setCleanupthreadinterval(int _cleanupthreadinterval) {
        this.cleanupthreadinterval = _cleanupthreadinterval;
    }

    /**
     *Sets the priority attribute of the StoreJanitorImpl object
     *
     *@param  _priority  The new priority value
     *@since
     */
    private void setPriority(int _priority) {
        this.priority = _priority;
    }

    /**
     *Sets the jVM attribute of the StoreJanitorImpl object
     *
     *@param  _runtime  The new jVM value
     *@since
     */
    private void setJVM(Runtime _runtime) {
        this.jvm = _runtime;
    }

    /**
     *Sets the storeList attribute of the StoreJanitorImpl object
     *
     *@param  _storelist  The new storeList value
     *@since
     */
    private void setStoreList(ArrayList _storelist) {
        this.storelist = _storelist;
    }

    // Renaming of parameters due to new functionality.
    /**
     *Gets the maxHeap attribute of the StoreJanitorImpl object
     *
     *@return    The maxHeap value
     *@since
     */
    private int getMaxHeap() {
        return heapsize;
    }

    /**
     *Gets the minFree attribute of the StoreJanitorImpl object
     *
     *@return    The minFree value
     *@since
     */
    private int getMinFree() {
        return freememory;
    }

    /**
     *Gets the maxSleep attribute of the StoreJanitorImpl object
     *
     *@return    The maxSleep value
     *@since
     */
    private int getMaxSleep() {
        return cleanupthreadinterval * 1000;
    }

    /**
     *Gets the reduceBy attribute of the StoreJanitorImpl object
     *
     *@return    The reduceBy value
     *@since
     */
    private int getReduceBy() {
        return m_percent;
    }

    /**
     *Gets the freememory attribute of the StoreJanitorImpl object
     *
     *@return    The freememory value
     *@since
     */
    private int getFreememory() {
        return freememory;
    }

    /**
     *Gets the heapsize attribute of the StoreJanitorImpl object
     *
     *@return    The heapsize value
     *@since
     */
    private int getHeapsize() {
        return this.heapsize;
    }

    /**
     *Gets the cleanupthreadinterval attribute of the StoreJanitorImpl object
     *
     *@return    The cleanupthreadinterval value
     *@since
     */
    private int getCleanupthreadinterval() {
        return this.cleanupthreadinterval;
    }

    /**
     *Gets the priority attribute of the StoreJanitorImpl object
     *
     *@return    The priority value
     *@since
     */
    private int getPriority() {
        return this.priority;
    }

    /**
     *Gets the jVM attribute of the StoreJanitorImpl object
     *
     *@return    The jVM value
     *@since
     */
    private Runtime getJVM() {
        return this.jvm;
    }

    /**
     *Gets the storeList attribute of the StoreJanitorImpl object
     *
     *@return    The storeList value
     *@since
     */
    private ArrayList getStoreList() {
        return this.storelist;
    }

    /**
     * Starting at the next store, removes items, moving to the next again if necessary, until the specified number of items have been removed or all the stores are empty.
     *
     *@since
     */
    private void attemptToFreeStorage() {
        int storeListSize = getStoreList().size();
        int remove = getReduceBy();
        debug("number of stores is " + storeListSize + ", number of items to be removed is " + remove + ", if possible!");
        incIndex();
        for (int cnt = 0; cnt < storeListSize; incIndex(), cnt++) {// Look in all stores if necessary.
            if ((remove = reduceStoreBy(remove)) == 0) {
                break;// Keep looking till all items removed,
            }
        }// or all stores are empty.
        if (remove < getReduceBy()) {// If items were removed call garbage collector.
            long gcTime = System.currentTimeMillis();
            jvm.gc();
            gcTime = System.currentTimeMillis() - gcTime;
            debug("items removed, so collecting garbage - took " + gcTime + "ms");
            debug("minFree=" + getMinFree() + ",      freeHeap=" + jvm.freeMemory() + ",          freeIsLow=" + freeIsLow());
        }
    }

    /**
     * Increment the store index.
     *
     *@since
     */
    private void incIndex() {
        if (++index >= getStoreList().size()) {
            index = 0;
        }
    }

    /**
     * Reduce the current store by the number of items specified, if possible.
     *
     *@param  remove  The number of items to be removed.
     *@return         the remaining count of items, that could not be removed.
     *@since
     */
    private int reduceStoreBy(int remove) {
        Store store = (Store) storelist.get(index);
        int sizeBefore = countSize(store);
        for (int i = 0; i < sizeBefore & remove > 0; i++, remove--) {
            store.free();
        }
        int sizeAfter = countSize(store);
        debug("store index=" + index + ", size before=" + sizeBefore + ",  size after=" + sizeAfter + ", removed=" + (sizeBefore - sizeAfter));
        return remove;
    }

    /**
     * To check if total memory is big enough to allow stores to be reduced.
     *
     *@return    true if big enough.
     *@since
     */
    private boolean heapIsBig() {
        return jvm.totalMemory() > getMaxHeap();
    }

    /**
     * To check if free memory is small enough to start reducing stores.
     *
     *@return    true if small enough.
     *@since
     */
    private boolean freeIsLow() {
        return jvm.freeMemory() < getMinFree();
    }

    /**
     * To calculate the minimum time in which the memory could be filled at the maximum rate of use.
     *
     *@return    the minimum time to fill.
     *@since
     */
    private long minTimeToFill() {
        return longDiv(jvm.freeMemory(), maxRateOfChange);
    }

    /**
     * Long division, guarding agains accidental divide by zero.
     *
     *@param  top     Description of Parameter
     *@param  bottom  Description of Parameter
     *@return         the result of division.
     *@since
     */
    private long longDiv(long top, long bottom) {
        try {
            return top / bottom;
        } catch (Exception e) {
            return top > 0 ? Long.MAX_VALUE : Long.MIN_VALUE;
        }
    }

    /**
     * Calculate the jvm memory in use now.
     *
     *@return    memory in use.
     *@since
     */
    private long inUseNow() {
        return jvm.totalMemory() - jvm.freeMemory();
    }

    /**
     * Count the size of a store.
     *
     *@param  store  Description of Parameter
     *@return        the size of the store.
     *@since
     */
    private int countSize(Store store) {
        int size = 0;
        Enumeration enum = store.keys();
        while (enum.hasMoreElements()) {
            size++;
            enum.nextElement();
        }
        return size;
    }

    /**
     * Shorten the call to print a debug message.
     *
     *@param  message  Description of Parameter
     *@since
     */
    private void debug(String message) {
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug(message);
        }
    }

}
TOP

Related Classes of org.apache.cocoon.storejanitor.StoreJanitorImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.