org.opencms.monitor.CmsMemoryMonitor.java Source code

Java tutorial

Introduction

Here is the source code for org.opencms.monitor.CmsMemoryMonitor.java

Source

/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software GmbH, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.monitor;

import org.opencms.cache.CmsLruCache;
import org.opencms.cache.CmsMemoryObjectCache;
import org.opencms.cache.CmsVfsMemoryObjectCache;
import org.opencms.configuration.CmsSystemConfiguration;
import org.opencms.db.CmsCacheSettings;
import org.opencms.db.CmsDriverManager;
import org.opencms.db.CmsPublishedResource;
import org.opencms.db.CmsSecurityManager;
import org.opencms.file.CmsFile;
import org.opencms.file.CmsGroup;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsProject;
import org.opencms.file.CmsProperty;
import org.opencms.file.CmsPropertyDefinition;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsUser;
import org.opencms.flex.CmsFlexCache.CmsFlexCacheVariation;
import org.opencms.i18n.CmsLocaleManager;
import org.opencms.lock.CmsLock;
import org.opencms.lock.CmsLockManager;
import org.opencms.mail.CmsMailTransport;
import org.opencms.mail.CmsSimpleMail;
import org.opencms.main.CmsEvent;
import org.opencms.main.CmsLog;
import org.opencms.main.CmsSessionManager;
import org.opencms.main.I_CmsEventListener;
import org.opencms.main.OpenCms;
import org.opencms.publish.CmsPublishHistory;
import org.opencms.publish.CmsPublishJobInfoBean;
import org.opencms.publish.CmsPublishQueue;
import org.opencms.scheduler.I_CmsScheduledJob;
import org.opencms.security.CmsAccessControlList;
import org.opencms.security.CmsOrganizationalUnit;
import org.opencms.security.CmsPermissionSet;
import org.opencms.security.CmsRole;
import org.opencms.security.I_CmsPermissionHandler;
import org.opencms.util.CmsCollectionsGenericWrapper;
import org.opencms.util.CmsDateUtil;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;
import org.opencms.util.PrintfFormat;
import org.opencms.xml.CmsXmlContentDefinition;
import org.opencms.xml.CmsXmlEntityResolver;

import java.util.ArrayList;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.mail.internet.InternetAddress;

import org.apache.commons.collections.Buffer;
import org.apache.commons.collections.buffer.SynchronizedBuffer;
import org.apache.commons.collections.map.LRUMap;
import org.apache.commons.logging.Log;

/**
 * Monitors OpenCms memory consumption.<p>
 * 
 * The memory monitor also provides all kind of caches used in the OpenCms core.<p>
 * 
 * @since 6.0.0 
 */
public class CmsMemoryMonitor implements I_CmsScheduledJob {

    /** Cache types. */
    public enum CacheType {
        /** Access Control Lists cache. */
        ACL,
        /** Content Definition cache. */
        CONTENT_DEFINITION,
        /** Group cache. */
        GROUP,
        /** Has Role cache. */
        HAS_ROLE,
        /** Locale cache. */
        LOCALE,
        /** Lock cache. */
        LOCK,
        /** Memory Object cache. */
        MEMORY_OBJECT,
        /** Organizational Unit cache. */
        ORG_UNIT,
        /** Permission cache. */
        PERMISSION,
        /** Offline Project cache. */
        PROJECT,
        /** Project resources cache. */
        PROJECT_RESOURCES,
        /** Property cache. */
        PROPERTY,
        /** Property List cache. */
        PROPERTY_LIST,
        /** Publish history cache. */
        PUBLISH_HISTORY,
        /** Publish queue cache. */
        PUBLISH_QUEUE,
        /** Published resources cache. */
        PUBLISHED_RESOURCES,
        /** Resource cache. */
        RESOURCE,
        /** Resource List cache. */
        RESOURCE_LIST,
        /** Role List cache. */
        ROLE_LIST,
        /** User cache. */
        USER,
        /** User list cache. */
        USER_LIST,
        /** User Groups cache. */
        USERGROUPS,
        /** VFS Object cache. */
        VFS_OBJECT,
        /** XML Entity Permanent cache. */
        XML_ENTITY_PERM,
        /** XML Entity Temporary cache. */
        XML_ENTITY_TEMP;
    }

    /** Set interval for clearing the caches to 10 minutes. */
    private static final int INTERVAL_CLEAR = 1000 * 60 * 10;

    /** The log object for this class. */
    private static final Log LOG = CmsLog.getLog(CmsMemoryMonitor.class);

    /** Flag indicating if monitor is currently running. */
    private static boolean m_currentlyRunning;

    /** Maximum depth for object size recursion. */
    private static final int MAX_DEPTH = 5;

    /** Cache for access control lists. */
    private Map<String, CmsAccessControlList> m_cacheAccessControlList;

    /** A temporary cache for XML content definitions. */
    private Map<String, CmsXmlContentDefinition> m_cacheContentDefinitions;

    /** Cache for groups. */
    private Map<String, CmsGroup> m_cacheGroup;

    /** Cache for roles. */
    private Map<String, Boolean> m_cacheHasRoles;

    /** A cache for accelerated locale lookup. */
    private Map<String, Locale> m_cacheLocale;

    /** Cache for the resource locks. */
    private Map<String, CmsLock> m_cacheLock;

    /** The memory object cache map. */
    private Map<String, Object> m_cacheMemObject;

    /** Cache for organizational units. */
    private Map<String, CmsOrganizationalUnit> m_cacheOrgUnit;

    /** Cache for permission checks. */
    private Map<String, I_CmsPermissionHandler.CmsPermissionCheckResult> m_cachePermission;

    /** Cache for offline projects. */
    private Map<String, CmsProject> m_cacheProject;

    /** Cache for project resources. */
    private Map<String, List<CmsResource>> m_cacheProjectResources;

    /** Cache for properties. */
    private Map<String, CmsProperty> m_cacheProperty;

    /** Cache for property lists. */
    private Map<String, List<CmsProperty>> m_cachePropertyList;

    /** Cache for published resources. */
    private Map<String, List<CmsPublishedResource>> m_cachePublishedResources;

    /** Cache for resources. */
    private Map<String, CmsResource> m_cacheResource;

    /** Cache for resource lists. */
    private Map<String, List<CmsResource>> m_cacheResourceList;

    /** Cache for role lists. */
    private Map<String, List<CmsRole>> m_cacheRoleLists;

    /** Cache for user data. */
    private Map<String, CmsUser> m_cacheUser;

    /** Cache for user groups. */
    private Map<String, List<CmsGroup>> m_cacheUserGroups;

    /** Cache for user lists. */
    private Map<String, List<CmsUser>> m_cacheUserList;

    /** The vfs memory cache map. */
    private Map<String, Object> m_cacheVfsObject;

    /** A permanent cache to avoid multiple readings of often used files from the VFS. */
    private Map<String, byte[]> m_cacheXmlPermanentEntity;

    /** A temporary cache to avoid multiple readings of often used files from the VFS. */
    private Map<String, byte[]> m_cacheXmlTemporaryEntity;

    /** The memory monitor configuration. */
    private CmsMemoryMonitorConfiguration m_configuration;

    /** Map to keep track of disabled caches. */
    private Map<CacheType, Boolean> m_disabled = new HashMap<CacheType, Boolean>();

    /** Interval in which emails are send. */
    private int m_intervalEmail;

    /** Interval in which the log is written. */
    private int m_intervalLog;

    /** Interval between 2 warnings. */
    private int m_intervalWarning;

    /** The time the caches were last cleared. */
    private long m_lastClearCache;

    /** The time the last status email was send. */
    private long m_lastEmailStatus;

    /** The time the last warning email was send. */
    private long m_lastEmailWarning;

    /** The time the last status log was written. */
    private long m_lastLogStatus;

    /** The time the last warning log was written. */
    private long m_lastLogWarning;

    /** The number of times the log entry was written. */
    private int m_logCount;

    /** Memory percentage to reach to go to warning level. */
    private int m_maxUsagePercent;

    /** The average memory status. */
    private CmsMemoryStatus m_memoryAverage;

    /** The current memory status. */
    private CmsMemoryStatus m_memoryCurrent;

    /** Contains the object to be monitored. */
    private Map<String, Object> m_monitoredObjects;

    /** Buffer for publish history. */
    private Buffer m_publishHistory;

    /** Buffer for publish jobs. */
    private Buffer m_publishQueue;

    /** Flag for memory warning mail send. */
    private boolean m_warningLoggedSinceLastStatus;

    /** Flag for memory warning mail send. */
    private boolean m_warningSendSinceLastStatus;

    /**
     * Empty constructor, required by OpenCms scheduler.<p>
     */
    public CmsMemoryMonitor() {

        m_monitoredObjects = new HashMap<String, Object>();
    }

    /**
     * Returns the size of objects that are instances of
     * <code>byte[]</code>, <code>String</code>, <code>CmsFile</code>,<code>I_CmsLruCacheObject</code>.<p>
     * For other objects, a size of 0 is returned.
     * 
     * @param obj the object
     * @return the size of the object 
     */
    public static int getMemorySize(Object obj) {

        if (obj instanceof I_CmsMemoryMonitorable) {
            return ((I_CmsMemoryMonitorable) obj).getMemorySize();
        }

        if (obj instanceof byte[]) {
            // will always be a total of 16 + 8
            return 8 + (int) (Math.ceil(((byte[]) obj).length / 16.0) * 16.0);
        }

        if (obj instanceof String) {
            // will always be a total of 16 + 24
            return 24 + (int) (Math.ceil(((String) obj).length() / 8.0) * 16.0);
        }

        if (obj instanceof CmsFile) {
            CmsFile f = (CmsFile) obj;
            if (f.getContents() != null) {
                return f.getContents().length + 1024;
            } else {
                return 1024;
            }
        }

        if (obj instanceof CmsUUID) {
            return 184; // worst case if UUID String has been generated
        }

        if (obj instanceof CmsPermissionSet) {
            return 16; // two ints
        }

        if (obj instanceof CmsResource) {
            return 1024; // estimated size
        }

        if (obj instanceof CmsPublishedResource) {
            return 512; // estimated size
        }

        if (obj instanceof CmsUser) {
            return 2048; // estimated size
        }

        if (obj instanceof CmsGroup) {
            return 512; // estimated size
        }

        if (obj instanceof CmsProject) {
            return 512; // estimated size
        }

        if (obj instanceof Boolean) {
            return 8; // one boolean
        }

        if (obj instanceof CmsProperty) {
            int size = 8;

            CmsProperty property = (CmsProperty) obj;
            size += getMemorySize(property.getName());

            if (property.getResourceValue() != null) {
                size += getMemorySize(property.getResourceValue());
            }

            if (property.getStructureValue() != null) {
                size += getMemorySize(property.getStructureValue());
            }

            if (property.getOrigin() != null) {
                size += getMemorySize(property.getOrigin());
            }

            return size;
        }

        if (obj instanceof CmsPropertyDefinition) {
            int size = 8;

            CmsPropertyDefinition propDef = (CmsPropertyDefinition) obj;
            size += getMemorySize(propDef.getName());
            size += getMemorySize(propDef.getId());

            return size;
        }

        return 8;
    }

    /**
     * Returns the total value size of a list object.<p>
     * 
     * @param listValue the list object
     * @param depth the max recursion depth for calculation the size
     * 
     * @return the size of the list object
     */
    public static long getValueSize(List<?> listValue, int depth) {

        long totalSize = 0;
        try {
            Object[] values = listValue.toArray();
            for (int i = 0, s = values.length; i < s; i++) {

                Object obj = values[i];

                if (obj instanceof CmsAccessControlList) {
                    obj = ((CmsAccessControlList) obj).getPermissionMap();
                }

                if (obj instanceof CmsFlexCacheVariation) {
                    obj = ((CmsFlexCacheVariation) obj).m_map;
                }

                if ((obj instanceof Map) && (depth < MAX_DEPTH)) {
                    totalSize += getValueSize((Map<?, ?>) obj, depth + 1);
                    continue;
                }

                if ((obj instanceof List) && (depth < MAX_DEPTH)) {
                    totalSize += getValueSize((List<?>) obj, depth + 1);
                    continue;
                }

                totalSize += getMemorySize(obj);
            }
        } catch (ConcurrentModificationException e) {
            // this might happen since even the .toArray() method internally creates an iterator
        } catch (Throwable t) {
            // catch all other exceptions otherwise the whole monitor will stop working
            if (LOG.isDebugEnabled()) {
                LOG.debug(Messages.get().getBundle().key(Messages.LOG_CAUGHT_THROWABLE_1, t.getMessage()));
            }
        }

        return totalSize;
    }

    /**
     * Returns the total value size of a map object.<p>
     * 
     * @param mapValue the map object
     * @param depth the max recursion depth for calculation the size
     * 
     * @return the size of the map object
     */
    public static long getValueSize(Map<?, ?> mapValue, int depth) {

        long totalSize = 0;
        try {
            Object[] values = mapValue.values().toArray();
            for (int i = 0, s = values.length; i < s; i++) {

                Object obj = values[i];

                if (obj instanceof CmsAccessControlList) {
                    obj = ((CmsAccessControlList) obj).getPermissionMap();
                }

                if (obj instanceof CmsFlexCacheVariation) {
                    obj = ((CmsFlexCacheVariation) obj).m_map;
                }

                if ((obj instanceof Map) && (depth < MAX_DEPTH)) {
                    totalSize += getValueSize((Map<?, ?>) obj, depth + 1);
                    continue;
                }

                if ((obj instanceof List) && (depth < MAX_DEPTH)) {
                    totalSize += getValueSize((List<?>) obj, depth + 1);
                    continue;
                }

                totalSize += getMemorySize(obj);
            }
        } catch (ConcurrentModificationException e) {
            // this might happen since even the .toArray() method internally creates an iterator
        } catch (Throwable t) {
            // catch all other exceptions otherwise the whole monitor will stop working
            if (LOG.isDebugEnabled()) {
                LOG.debug(Messages.get().getBundle().key(Messages.LOG_CAUGHT_THROWABLE_1, t.getMessage()));
            }
        }

        return totalSize;
    }

    /**
     * Returns the value sizes of value objects within the monitored object.<p>
     * 
     * @param obj the object 
     * 
     * @return the value sizes of value objects or "-"-fields
     */
    public static long getValueSize(Object obj) {

        if (obj instanceof CmsLruCache) {
            return ((CmsLruCache) obj).size();
        }

        if (obj instanceof Map) {
            return getValueSize((Map<?, ?>) obj, 1);
        }

        if (obj instanceof List) {
            return getValueSize((List<?>) obj, 1);
        }

        try {
            return getMemorySize(obj);
        } catch (Exception exc) {
            return 0;
        }
    }

    /**
     * Caches the given acl under the given cache key.<p>
     * 
     * @param key the cache key
     * @param acl the acl to cache
     */
    public void cacheACL(String key, CmsAccessControlList acl) {

        if (m_disabled.get(CacheType.ACL) != null) {
            return;
        }
        m_cacheAccessControlList.put(key, acl);
    }

    /**
     * Caches the given content definition under the given cache key.<p>
     * 
     * @param key the cache key
     * @param contentDefinition the content definition to cache
     */
    public void cacheContentDefinition(String key, CmsXmlContentDefinition contentDefinition) {

        if (m_disabled.get(CacheType.CONTENT_DEFINITION) != null) {
            return;
        }
        m_cacheContentDefinitions.put(key, contentDefinition);
    }

    /**
     * Caches the given group under its id AND fully qualified name.<p>
     * 
     * @param group the group to cache
     */
    public void cacheGroup(CmsGroup group) {

        if (m_disabled.get(CacheType.GROUP) != null) {
            return;
        }
        m_cacheGroup.put(group.getId().toString(), group);
        m_cacheGroup.put(group.getName(), group);
    }

    /**
     * Caches the given locale under the given cache key.<p>
     * 
     * @param key the cache key
     * @param locale the locale to cache
     */
    public void cacheLocale(String key, Locale locale) {

        if (m_cacheLocale != null) {
            if (m_disabled.get(CacheType.LOCALE) != null) {
                return;
            }
            // this may be accessed before initialization
            m_cacheLocale.put(key, locale);
        }
    }

    /**
     * Caches the given lock.<p>
     *
     * The lock is cached by it resource's root path.<p>
     * 
     * @param lock the lock to cache
     */
    public void cacheLock(CmsLock lock) {

        if (m_disabled.get(CacheType.LOCK) != null) {
            return;
        }
        m_cacheLock.put(lock.getResourceName(), lock);
    }

    /**
     * Caches the given object under the given cache key.<p>
     * 
     * @param key the cache key
     * @param obj the object to cache
     */
    public void cacheMemObject(String key, Object obj) {

        if (m_disabled.get(CacheType.MEMORY_OBJECT) != null) {
            return;
        }
        m_cacheMemObject.put(key, obj);
    }

    /**
     * Caches the given organizational under its id AND the fully qualified name.<p>
     * 
     * @param orgUnit the organizational unit to cache
     */
    public void cacheOrgUnit(CmsOrganizationalUnit orgUnit) {

        if (m_disabled.get(CacheType.ORG_UNIT) != null) {
            return;
        }
        m_cacheOrgUnit.put(orgUnit.getId().toString(), orgUnit);
        m_cacheOrgUnit.put(orgUnit.getName(), orgUnit);
    }

    /**
     * Caches the given permission check result under the given cache key.<p>
     * 
     * @param key the cache key
     * @param permission the permission check result to cache
     */
    public void cachePermission(String key, I_CmsPermissionHandler.CmsPermissionCheckResult permission) {

        if (m_disabled.get(CacheType.PERMISSION) != null) {
            return;
        }
        m_cachePermission.put(key, permission);
    }

    /**
     * Caches the given project under its id AND the fully qualified name.<p>
     * 
     * @param project the project to cache
     */
    public void cacheProject(CmsProject project) {

        if (m_disabled.get(CacheType.PROJECT) != null) {
            return;
        }
        m_cacheProject.put(project.getUuid().toString(), project);
        m_cacheProject.put(project.getName(), project);
    }

    /**
     * Caches the given project resource list under the given cache key.<p>
     * 
     * @param key the cache key
     * @param projectResources the project resources to cache
     */
    public void cacheProjectResources(String key, List<CmsResource> projectResources) {

        if (m_disabled.get(CacheType.PROJECT_RESOURCES) != null) {
            return;
        }
        m_cacheProjectResources.put(key, projectResources);
    }

    /**
     * Caches the given property under the given cache key.<p>
     * 
     * @param key the cache key
     * @param property the property to cache
     */
    public void cacheProperty(String key, CmsProperty property) {

        if (m_disabled.get(CacheType.PROPERTY) != null) {
            return;
        }
        m_cacheProperty.put(key, property);
    }

    /**
     * Caches the given property list under the given cache key.<p>
     * 
     * @param key the cache key
     * @param propertyList the property list to cache
     */
    public void cachePropertyList(String key, List<CmsProperty> propertyList) {

        if (m_disabled.get(CacheType.PROPERTY_LIST) != null) {
            return;
        }
        m_cachePropertyList.put(key, propertyList);
    }

    /**
     * Caches the given published resources list under the given cache key.<p>
     * 
     * @param cacheKey the cache key
     * @param publishedResources the published resources list to cache
     */
    public void cachePublishedResources(String cacheKey, List<CmsPublishedResource> publishedResources) {

        if (m_disabled.get(CacheType.PUBLISHED_RESOURCES) != null) {
            return;
        }
        m_cachePublishedResources.put(cacheKey, publishedResources);
    }

    /**
     * Caches the given publish job.<p>
     * 
     * @param publishJob the publish job
     */
    public void cachePublishJob(CmsPublishJobInfoBean publishJob) {

        if (m_disabled.get(CacheType.PUBLISH_QUEUE) != null) {
            return;
        }
        m_publishQueue.add(publishJob);
    }

    /**
     * Caches the given publish job in the publish job history.<p>
     * 
     * @param publishJob the publish job
     */
    public void cachePublishJobInHistory(CmsPublishJobInfoBean publishJob) {

        if (m_disabled.get(CacheType.PUBLISH_HISTORY) != null) {
            return;
        }
        m_publishHistory.add(publishJob);
    }

    /**
     * Caches the given resource under the given cache key.<p>
     * 
     * @param key the cache key
     * @param resource the resource to cache
     */
    public void cacheResource(String key, CmsResource resource) {

        if (m_disabled.get(CacheType.RESOURCE) != null) {
            return;
        }
        m_cacheResource.put(key, resource);
    }

    /**
     * Caches the given resource list under the given cache key.<p>
     * 
     * @param key the cache key
     * @param resourceList the resource list to cache
     */
    public void cacheResourceList(String key, List<CmsResource> resourceList) {

        if (m_disabled.get(CacheType.RESOURCE_LIST) != null) {
            return;
        }
        m_cacheResourceList.put(key, resourceList);
    }

    /**
     * Caches the given value under the given cache key.<p>
     * 
     * @param key the cache key
     * @param hasRole if the user has the given role
     */
    public void cacheRole(String key, boolean hasRole) {

        if (m_disabled.get(CacheType.HAS_ROLE) != null) {
            return;
        }
        m_cacheHasRoles.put(key, Boolean.valueOf(hasRole));
    }

    /**
     * Caches the given value under the given cache key.<p>
     * 
     * @param key the cache key
     * @param roles the roles of the user
     */
    public void cacheRoleList(String key, List<CmsRole> roles) {

        if (m_disabled.get(CacheType.ROLE_LIST) != null) {
            return;
        }
        m_cacheRoleLists.put(key, roles);
    }

    /**
     * Caches the given user under its id AND the fully qualified name.<p>
     * 
     * @param user the user to cache
     */
    public void cacheUser(CmsUser user) {

        if (m_disabled.get(CacheType.USER) != null) {
            return;
        }
        m_cacheUser.put(user.getId().toString(), user);
        m_cacheUser.put(user.getName(), user);
    }

    /**
     * Caches the given list of user groups under the given cache key.<p>
     * 
     * @param key the cache key
     * @param userGroups the list of user groups to cache
     */
    public void cacheUserGroups(String key, List<CmsGroup> userGroups) {

        if (m_disabled.get(CacheType.USERGROUPS) != null) {
            return;
        }
        m_cacheUserGroups.put(key, userGroups);
    }

    /**
     * Caches the given list of users under the given cache key.<p>
     * 
     * @param key the cache key
     * @param userList the list of users to cache
     */
    public void cacheUserList(String key, List<CmsUser> userList) {

        if (m_disabled.get(CacheType.USER_LIST) != null) {
            return;
        }
        m_cacheUserList.put(key, userList);
    }

    /**
     * Caches the given vfs object under the given cache key.<p>
     * 
     * @param key the cache key
     * @param obj the vfs object to cache
     */
    public void cacheVfsObject(String key, Object obj) {

        if (m_disabled.get(CacheType.VFS_OBJECT) != null) {
            return;
        }
        m_cacheVfsObject.put(key, obj);
    }

    /**
     * Caches the given xml entity under the given system id.<p>
     * 
     * @param systemId the cache key
     * @param content the content to cache
     */
    public void cacheXmlPermanentEntity(String systemId, byte[] content) {

        if (m_disabled.get(CacheType.XML_ENTITY_PERM) != null) {
            return;
        }
        m_cacheXmlPermanentEntity.put(systemId, content);
    }

    /**
     * Caches the given xml entity under the given cache key.<p>
     * 
     * @param key the cache key
     * @param content the content to cache
     */
    public void cacheXmlTemporaryEntity(String key, byte[] content) {

        if (m_disabled.get(CacheType.XML_ENTITY_TEMP) != null) {
            return;
        }
        m_cacheXmlTemporaryEntity.put(key, content);
    }

    /**
     * Clears the access control list cache when access control entries are changed.<p>
     */
    public void clearAccessControlListCache() {

        flushCache(CacheType.ACL);
        flushCache(CacheType.PERMISSION);
        clearResourceCache();
    }

    /**
     * Clears almost all internal caches.<p>
     */
    public void clearCache() {

        clearPrincipalsCache();

        flushCache(CacheType.PROJECT);
        flushCache(CacheType.RESOURCE);
        flushCache(CacheType.RESOURCE_LIST);
        flushCache(CacheType.PROPERTY);
        flushCache(CacheType.PROPERTY_LIST);
        flushCache(CacheType.PROJECT_RESOURCES);
        flushCache(CacheType.PUBLISHED_RESOURCES);
    }

    /**
     * Clears all internal principal-related caches.<p>
     */
    public void clearPrincipalsCache() {

        flushCache(CacheType.USER);
        flushCache(CacheType.GROUP);
        flushCache(CacheType.ORG_UNIT);
        flushCache(CacheType.ACL);
        flushCache(CacheType.PERMISSION);
        flushCache(CacheType.HAS_ROLE);
        flushCache(CacheType.ROLE_LIST);
        flushCache(CacheType.USERGROUPS);
        flushCache(CacheType.USER_LIST);
    }

    /**
     * Clears all the depending caches when a resource was changed.<p>
     */
    public void clearResourceCache() {

        flushCache(CacheType.RESOURCE);
        flushCache(CacheType.RESOURCE_LIST);
        flushCache(CacheType.HAS_ROLE);
        flushCache(CacheType.ROLE_LIST);
    }

    /**
     * Clears the user cache for the given user.<p>
     * 
     * @param user the user
     */
    public void clearUserCache(CmsUser user) {

        uncacheUser(user);
        flushCache(CacheType.RESOURCE_LIST);
    }

    /**
     * Disables the given cache.<p>
     * 
     * @param types the cache type to disable
     */
    public void disableCache(CacheType... types) {

        for (CacheType type : types) {
            m_disabled.put(type, Boolean.TRUE);
        }
        flushCache(types);
    }

    /**
     * Enables the given cache.<p>
     * 
     * @param types the cache type to disable
     */
    public void enableCache(CacheType... types) {

        for (CacheType type : types) {
            m_disabled.remove(type);
        }
    }

    /**
     * Returns if monitoring is enabled.<p>
     * 
     * @return true if monitoring is enabled
     */
    public boolean enabled() {

        return true;
    }

    /**
     * Flushes the ACL cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushACLs() {

        flushCache(CacheType.ACL);
    }

    /**
     * Flushes the given cache.<p>
     * 
     * @param types the cache types to flush
     */
    public void flushCache(CacheType... types) {

        for (CacheType type : types) {
            switch (type) {
            case ACL:
                m_cacheAccessControlList.clear();
                break;
            case CONTENT_DEFINITION:
                m_cacheContentDefinitions.clear();
                break;
            case GROUP:
                m_cacheGroup.clear();
                break;
            case HAS_ROLE:
                m_cacheHasRoles.clear();
                break;
            case LOCALE:
                m_cacheLocale.clear();
                break;
            case LOCK:
                m_cacheLock.clear();
                break;
            case MEMORY_OBJECT:
                m_cacheMemObject.clear();
                break;
            case ORG_UNIT:
                m_cacheOrgUnit.clear();
                break;
            case PERMISSION:
                m_cachePermission.clear();
                break;
            case PROJECT:
                m_cacheProject.clear();
                break;
            case PROJECT_RESOURCES:
                m_cacheProjectResources.clear();
                break;
            case PROPERTY:
                m_cacheProperty.clear();
                break;
            case PROPERTY_LIST:
                m_cachePropertyList.clear();
                break;
            case PUBLISHED_RESOURCES:
                m_cachePublishedResources.clear();
                break;
            case PUBLISH_HISTORY:
                m_publishHistory.clear();
                break;
            case PUBLISH_QUEUE:
                m_publishQueue.clear();
                break;
            case RESOURCE:
                m_cacheResource.clear();
                break;
            case RESOURCE_LIST:
                m_cacheResourceList.clear();
                break;
            case ROLE_LIST:
                m_cacheRoleLists.clear();
                break;
            case USER:
                m_cacheUser.clear();
                break;
            case USERGROUPS:
                m_cacheUserGroups.clear();
                break;
            case USER_LIST:
                m_cacheUserList.clear();
                break;
            case VFS_OBJECT:
                m_cacheVfsObject.clear();
                break;
            case XML_ENTITY_PERM:
                m_cacheXmlPermanentEntity.clear();
                break;
            case XML_ENTITY_TEMP:
                m_cacheXmlTemporaryEntity.clear();
                break;
            default:
                // can't happen
            }
        }
    }

    /**
     * Flushes the xml content definitions cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushContentDefinitions() {

        flushCache(CacheType.CONTENT_DEFINITION);
    }

    /**
     * Flushes the group cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushGroups() {

        flushCache(CacheType.GROUP);
    }

    /**
     * Flushes the locale cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushLocales() {

        flushCache(CacheType.LOCALE);
    }

    /**
     * Flushes the locks cache.<p>
     * 
     * @param newLocks if not <code>null</code> the lock cache is replaced by the given map 
     */
    public void flushLocks(Map<String, CmsLock> newLocks) {

        if ((newLocks == null) || newLocks.isEmpty()) {
            flushCache(CacheType.LOCK);
            return;
        }
        // initialize new lock cache
        Map<String, CmsLock> newLockCache = Collections.synchronizedMap(newLocks);
        // register it
        register(CmsLockManager.class.getName(), newLockCache);
        // save the old cache
        Map<String, CmsLock> oldCache = m_cacheLock;
        // replace the old by the new cache
        m_cacheLock = newLockCache;
        // clean up the old cache
        oldCache.clear();
    }

    /**
     * Flushes the memory object cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushMemObjects() {

        flushCache(CacheType.MEMORY_OBJECT);
    }

    /**
     * Flushes the organizational unit cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushOrgUnits() {

        flushCache(CacheType.ORG_UNIT);
    }

    /**
     * Flushes the permission check result cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushPermissions() {

        flushCache(CacheType.PERMISSION);
    }

    /**
     * Flushes the project resources cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushProjectResources() {

        flushCache(CacheType.PROJECT_RESOURCES);
    }

    /**
     * Flushes the project cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushProjects() {

        flushCache(CacheType.PROJECT);
    }

    /**
     * Flushes the property cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushProperties() {

        flushCache(CacheType.PROPERTY);
    }

    /**
     * Flushes the property list cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushPropertyLists() {

        flushCache(CacheType.PROPERTY_LIST);
    }

    /**
     * Flushes the published resources cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushPublishedResources() {

        flushCache(CacheType.PUBLISHED_RESOURCES);
    }

    /**
     * Flushes the publish history.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushPublishJobHistory() {

        flushCache(CacheType.PUBLISH_HISTORY);
    }

    /**
     * Flushes the publish queue.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushPublishJobs() {

        flushCache(CacheType.PUBLISH_QUEUE);
    }

    /**
     * Flushes the resource list cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushResourceLists() {

        flushCache(CacheType.RESOURCE_LIST);
    }

    /**
     * Flushes the resource cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushResources() {

        flushCache(CacheType.RESOURCE);
    }

    /**
     * Flushes the role lists cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushRoleLists() {

        flushCache(CacheType.ROLE_LIST);
    }

    /**
     * Flushes the roles cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushRoles() {

        flushCache(CacheType.HAS_ROLE);
    }

    /**
     * Flushes the user groups cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushUserGroups() {

        flushCache(CacheType.USERGROUPS);
        flushCache(CacheType.USER_LIST);
    }

    /**
     * Flushes the users cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushUsers() {

        flushCache(CacheType.USER);
    }

    /**
     * Flushes the vfs object cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushVfsObjects() {

        flushCache(CacheType.VFS_OBJECT);
    }

    /**
     * Flushes the xml permanent entities cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushXmlPermanentEntities() {

        flushCache(CacheType.XML_ENTITY_PERM);
    }

    /**
     * Flushes the xml temporary entities cache.<p>
     * 
     * @deprecated use {@link #flushCache(CacheType[])} instead
     */
    @Deprecated
    public void flushXmlTemporaryEntities() {

        flushCache(CacheType.XML_ENTITY_TEMP);
    }

    /**
     * Returns all cached lock root paths.<p>
     * 
     * @return a list of {@link String} objects
     */
    public List<String> getAllCachedLockPaths() {

        return new ArrayList<String>(m_cacheLock.keySet());
    }

    /**
     * Returns all cached locks.<p>
     * 
     * @return a list of {@link CmsLock} objects
     */
    public List<CmsLock> getAllCachedLocks() {

        return new ArrayList<CmsLock>(m_cacheLock.values());
    }

    /**
     * Returns all cached publish jobs in the queue as ordered list.<p>
     * 
     * @return all cached publish jobs
     */
    public List<CmsPublishJobInfoBean> getAllCachedPublishJobs() {

        return new ArrayList<CmsPublishJobInfoBean>(m_publishQueue);
    }

    /**
     * Returns all cached publish jobs in the history as ordered list.<p>
     * 
     * @return all cached publish jobs
     */
    public List<CmsPublishJobInfoBean> getAllCachedPublishJobsInHistory() {

        return new ArrayList<CmsPublishJobInfoBean>(m_publishHistory);
    }

    /**
     * Returns the ACL cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the ACL cached with the given cache key
     */
    public CmsAccessControlList getCachedACL(String key) {

        return m_cacheAccessControlList.get(key);
    }

    /**
     * Returns the xml content definition cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the xml content definition cached with the given cache key
     */
    public CmsXmlContentDefinition getCachedContentDefinition(String key) {

        return m_cacheContentDefinitions.get(key);
    }

    /**
     * Returns the group cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for, this may be the group's uuid or the fqn
     * 
     * @return the group cached with the given cache key
     */
    public CmsGroup getCachedGroup(String key) {

        return m_cacheGroup.get(key);
    }

    /**
     * Returns the locale cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the locale cached with the given cache key
     */
    public Locale getCachedLocale(String key) {

        if (m_cacheLocale == null) {
            // this may be accessed before initialization
            return null;
        }
        return m_cacheLocale.get(key);
    }

    /**
     * Returns the lock cached with the given root path or <code>null</code> if not found.<p>
     * 
     * @param rootPath the root path to look for
     * 
     * @return the lock cached with the given root path
     */
    public CmsLock getCachedLock(String rootPath) {

        return m_cacheLock.get(rootPath);
    }

    /**
     * Returns the memory object cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the memory object cached with the given cache key
     */
    public Object getCachedMemObject(String key) {

        return m_cacheMemObject.get(key);
    }

    /**
     * Returns the organizational unit cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for, this may be the organizational unit's uuid or the fqn
     * 
     * @return the organizational unit cached with the given cache key
     */
    public CmsOrganizationalUnit getCachedOrgUnit(String key) {

        return m_cacheOrgUnit.get(key);
    }

    /**
     * Returns the permission check result cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the permission check result cached with the given cache key
     */
    public I_CmsPermissionHandler.CmsPermissionCheckResult getCachedPermission(String key) {

        return m_cachePermission.get(key);
    }

    /**
     * Returns the project cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for, this may be the project's uuid or the fqn
     * 
     * @return the project cached with the given cache key
     */
    public CmsProject getCachedProject(String key) {

        return m_cacheProject.get(key);
    }

    /**
     * Returns the project resources list cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the project resources list cached with the given cache key
     */
    public List<CmsResource> getCachedProjectResources(String key) {

        return m_cacheProjectResources.get(key);
    }

    /**
     * Returns the property cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the property cached with the given cache key
     */
    public CmsProperty getCachedProperty(String key) {

        return m_cacheProperty.get(key);
    }

    /**
     * Returns the property list cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the property list cached with the given cache key
     */
    public List<CmsProperty> getCachedPropertyList(String key) {

        return m_cachePropertyList.get(key);
    }

    /**
     * Returns the published resources list cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param cacheKey the cache key to look for
     * 
     * @return the published resources list cached with the given cache key
     */
    public List<CmsPublishedResource> getCachedPublishedResources(String cacheKey) {

        return m_cachePublishedResources.get(cacheKey);
    }

    /**
     * Returns the publish job with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key  the cache key to look for
     * 
     * @return the publish job with the given cache key
     */
    public CmsPublishJobInfoBean getCachedPublishJob(String key) {

        synchronized (m_publishQueue) {
            for (Iterator<CmsPublishJobInfoBean> i = m_publishQueue.iterator(); i.hasNext();) {
                CmsPublishJobInfoBean publishJob = i.next();
                if (publishJob.getPublishHistoryId().toString().equals(key)) {
                    return publishJob;
                }
            }
        }
        return null;
    }

    /**
     * Returns the publish job from the history with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key  the cache key to look for
     * 
     * @return the publish job with the given cache key
     */
    public CmsPublishJobInfoBean getCachedPublishJobInHistory(String key) {

        for (Iterator<CmsPublishJobInfoBean> i = m_publishHistory.iterator(); i.hasNext();) {
            CmsPublishJobInfoBean publishJob = i.next();
            if (publishJob.getPublishHistoryId().toString().equals(key)) {
                return publishJob;
            }
        }

        return null;
    }

    /**
     * Returns the resource cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the resource cached with the given cache key
     */
    public CmsResource getCachedResource(String key) {

        return m_cacheResource.get(key);
    }

    /**
     * Returns the resource list cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the resource list cached with the given cache key
     */
    public List<CmsResource> getCachedResourceList(String key) {

        return m_cacheResourceList.get(key);
    }

    /**
     * Returns the value cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return if the user has the given role
     */
    public Boolean getCachedRole(String key) {

        return m_cacheHasRoles.get(key);
    }

    /**
     * Returns the value cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return list of roles
     */
    public List<CmsRole> getCachedRoleList(String key) {

        return m_cacheRoleLists.get(key);
    }

    /**
     * Returns the user cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for, this may be the user's uuid or the fqn
     * 
     * @return the user cached with the given cache key
     */
    public CmsUser getCachedUser(String key) {

        return m_cacheUser.get(key);
    }

    /**
     * Returns the user groups list cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the user groups list cached with the given cache key
     */
    public List<CmsGroup> getCachedUserGroups(String key) {

        return m_cacheUserGroups.get(key);
    }

    /**
     * Returns the user list cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the user groups list cached with the given cache key
     */
    public List<CmsUser> getCachedUserList(String key) {

        return m_cacheUserList.get(key);
    }

    /**
     * Returns the vfs object cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the vfs object cached with the given cache key
     */
    public Object getCachedVfsObject(String key) {

        return m_cacheVfsObject.get(key);
    }

    /**
     * Returns the xml permanent entity content cached with the given systemId or <code>null</code> if not found.<p>
     * 
     * @param systemId the cache key to look for
     * 
     * @return the xml permanent entity content cached with the given cache key
     */
    public byte[] getCachedXmlPermanentEntity(String systemId) {

        return m_cacheXmlPermanentEntity.get(systemId);
    }

    /**
     * Returns the xml temporary entity content cached with the given cache key or <code>null</code> if not found.<p>
     * 
     * @param key the cache key to look for
     * 
     * @return the xml temporary entity content cached with the given cache key
     */
    public byte[] getCachedXmlTemporaryEntity(String key) {

        return m_cacheXmlTemporaryEntity.get(key);
    }

    /**
     * Returns the configuration.<p>
     *
     * @return the configuration
     */
    public CmsMemoryMonitorConfiguration getConfiguration() {

        return m_configuration;
    }

    /**
     * Returns the next publish job from the publish job queue.<p>
     * 
     * @return the next publish job
     */
    public CmsPublishJobInfoBean getFirstCachedPublishJob() {

        synchronized (m_publishQueue) {
            if (!m_publishQueue.isEmpty()) {
                return (CmsPublishJobInfoBean) m_publishQueue.get();
            } else {
                return null;
            }
        }
    }

    /**
     * Returns the log count.<p>
     *
     * @return the log count
     */
    public int getLogCount() {

        return m_logCount;
    }

    /**
     * Returns the current memory status.<p>
     * 
     * @return the memory status
     */
    public CmsMemoryStatus getMemoryStatus() {

        m_memoryCurrent.update();
        return m_memoryCurrent;
    }

    /**
     * Initializes the monitor with the provided configuration.<p>
     * 
     * @param configuration the configuration to use
     */
    public void initialize(CmsSystemConfiguration configuration) {

        CmsCacheSettings cacheSettings = configuration.getCacheSettings();

        m_memoryAverage = new CmsMemoryStatus();
        m_memoryCurrent = new CmsMemoryStatus();

        m_warningSendSinceLastStatus = false;
        m_warningLoggedSinceLastStatus = false;
        m_lastEmailWarning = 0;
        m_lastEmailStatus = 0;
        m_lastLogStatus = 0;
        m_lastLogWarning = 0;
        m_lastClearCache = 0;
        m_configuration = configuration.getCmsMemoryMonitorConfiguration();

        m_intervalWarning = 720 * 60000;
        m_maxUsagePercent = 90;

        m_intervalEmail = m_configuration.getEmailInterval() * 1000;
        m_intervalLog = m_configuration.getLogInterval() * 1000;

        if (m_configuration.getWarningInterval() > 0) {
            m_intervalWarning = m_configuration.getWarningInterval();
        }
        m_intervalWarning *= 1000;

        if (m_configuration.getMaxUsagePercent() > 0) {
            m_maxUsagePercent = m_configuration.getMaxUsagePercent();
        }

        if (CmsLog.INIT.isInfoEnabled()) {
            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.LOG_MM_INTERVAL_LOG_1,
                    new Integer(m_intervalLog / 1000)));
            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.LOG_MM_INTERVAL_EMAIL_1,
                    new Integer(m_intervalEmail / 1000)));
            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.LOG_MM_INTERVAL_WARNING_1,
                    new Integer(m_intervalWarning / 1000)));
            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.LOG_MM_INTERVAL_MAX_USAGE_1,
                    new Integer(m_maxUsagePercent)));

            if ((m_configuration.getEmailReceiver() == null) || (m_configuration.getEmailSender() == null)) {
                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.LOG_MM_EMAIL_DISABLED_0));
            } else {
                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.LOG_MM_EMAIL_SENDER_1,
                        m_configuration.getEmailSender()));
                Iterator<String> i = m_configuration.getEmailReceiver().iterator();
                int n = 0;
                while (i.hasNext()) {
                    CmsLog.INIT.info(Messages.get().getBundle().key(Messages.LOG_MM_EMAIL_RECEIVER_2,
                            new Integer(n + 1), i.next()));
                    n++;
                }
            }
        }

        // create and register all system caches

        // temporary xml entities cache
        Map<String, byte[]> xmlTemporaryCache = CmsCollectionsGenericWrapper.createLRUMap(128);
        m_cacheXmlTemporaryEntity = Collections.synchronizedMap(xmlTemporaryCache);
        register(CmsXmlEntityResolver.class.getName() + ".xmlEntityTemporaryCache", m_cacheXmlTemporaryEntity);

        // permanent xml entities cache
        Map<String, byte[]> xmlPermanentCache = new HashMap<String, byte[]>(32);
        m_cacheXmlPermanentEntity = Collections.synchronizedMap(xmlPermanentCache);
        register(CmsXmlEntityResolver.class.getName() + ".xmlEntityPermanentCache", m_cacheXmlPermanentEntity);

        // xml content definitions cache
        Map<String, CmsXmlContentDefinition> contentDefinitionsCache = CmsCollectionsGenericWrapper
                .createLRUMap(64);
        m_cacheContentDefinitions = Collections.synchronizedMap(contentDefinitionsCache);
        register(CmsXmlEntityResolver.class.getName() + ".contentDefinitionsCache", m_cacheContentDefinitions);

        // lock cache
        Map<String, CmsLock> lockCache = new HashMap<String, CmsLock>();
        m_cacheLock = Collections.synchronizedMap(lockCache);
        register(CmsLockManager.class.getName(), lockCache);

        // locale cache
        Map<String, Locale> map = new HashMap<String, Locale>();
        m_cacheLocale = Collections.synchronizedMap(map);
        register(CmsLocaleManager.class.getName(), map);

        // permissions cache
        Map<String, I_CmsPermissionHandler.CmsPermissionCheckResult> lruPermissions = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getPermissionCacheSize());
        m_cachePermission = Collections.synchronizedMap(lruPermissions);
        register(CmsSecurityManager.class.getName(), lruPermissions);

        // user cache
        Map<String, CmsUser> lruUsers = CmsCollectionsGenericWrapper.createLRUMap(cacheSettings.getUserCacheSize());
        m_cacheUser = Collections.synchronizedMap(lruUsers);
        register(CmsDriverManager.class.getName() + ".userCache", lruUsers);

        // user list cache
        Map<String, List<CmsUser>> lruUserList = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getUserCacheSize());
        m_cacheUserList = Collections.synchronizedMap(lruUserList);
        register(CmsDriverManager.class.getName() + ".userListCache", lruUserList);

        // group cache
        Map<String, CmsGroup> lruGroup = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getGroupCacheSize());
        m_cacheGroup = Collections.synchronizedMap(lruGroup);
        register(CmsDriverManager.class.getName() + ".groupCache", lruGroup);

        // organizational unit cache
        Map<String, CmsOrganizationalUnit> lruOrgUnit = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getOrgUnitCacheSize());
        m_cacheOrgUnit = Collections.synchronizedMap(lruOrgUnit);
        register(CmsDriverManager.class.getName() + ".orgUnitCache", lruOrgUnit);

        // user groups list cache
        Map<String, List<CmsGroup>> lruUserGroups = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getUserGroupsCacheSize());
        m_cacheUserGroups = Collections.synchronizedMap(lruUserGroups);
        register(CmsDriverManager.class.getName() + ".userGroupsCache", lruUserGroups);

        // project cache
        Map<String, CmsProject> lruProjects = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getProjectCacheSize());
        m_cacheProject = Collections.synchronizedMap(lruProjects);
        register(CmsDriverManager.class.getName() + ".projectCache", lruProjects);

        // project resources cache cache
        Map<String, List<CmsResource>> lruProjectResources = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getProjectResourcesCacheSize());
        m_cacheProjectResources = Collections.synchronizedMap(lruProjectResources);
        register(CmsDriverManager.class.getName() + ".projectResourcesCache", lruProjectResources);

        // publish history
        int size = configuration.getPublishManager().getPublishHistorySize();
        Buffer buffer = CmsPublishHistory.getQueue(size);
        m_publishHistory = SynchronizedBuffer.decorate(buffer);
        register(CmsPublishHistory.class.getName() + ".publishHistory", buffer);

        // publish queue
        buffer = CmsPublishQueue.getQueue();
        m_publishQueue = SynchronizedBuffer.decorate(buffer);
        register(CmsPublishQueue.class.getName() + ".publishQueue", buffer);

        // resource cache
        Map<String, CmsResource> lruResources = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getResourceCacheSize());
        m_cacheResource = Collections.synchronizedMap(lruResources);
        register(CmsDriverManager.class.getName() + ".resourceCache", lruResources);

        // roles cache
        Map<String, Boolean> lruHasRoles = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getRolesCacheSize());
        m_cacheHasRoles = Collections.synchronizedMap(lruHasRoles);
        register(CmsDriverManager.class.getName() + ".rolesCache", lruHasRoles);

        // role lists cache
        Map<String, List<CmsRole>> lruRoleLists = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getRolesCacheSize());
        m_cacheRoleLists = Collections.synchronizedMap(lruRoleLists);
        register(CmsDriverManager.class.getName() + ".roleListsCache", lruRoleLists);

        // resource list cache
        Map<String, List<CmsResource>> lruResourceList = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getResourcelistCacheSize());
        m_cacheResourceList = Collections.synchronizedMap(lruResourceList);
        register(CmsDriverManager.class.getName() + ".resourceListCache", lruResourceList);

        // property cache
        Map<String, CmsProperty> lruProperty = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getPropertyCacheSize());
        m_cacheProperty = Collections.synchronizedMap(lruProperty);
        register(CmsDriverManager.class.getName() + ".propertyCache", lruProperty);

        // property list cache
        Map<String, List<CmsProperty>> lruPropertyList = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getPropertyListsCacheSize());
        m_cachePropertyList = Collections.synchronizedMap(lruPropertyList);
        register(CmsDriverManager.class.getName() + ".propertyListCache", lruPropertyList);

        // published resources list cache
        Map<String, List<CmsPublishedResource>> lruPublishedResources = CmsCollectionsGenericWrapper
                .createLRUMap(5);
        m_cachePublishedResources = Collections.synchronizedMap(lruPublishedResources);
        register(CmsDriverManager.class.getName() + ".publishedResourcesCache", lruPublishedResources);

        // acl cache
        Map<String, CmsAccessControlList> lruAcl = CmsCollectionsGenericWrapper
                .createLRUMap(cacheSettings.getAclCacheSize());
        m_cacheAccessControlList = Collections.synchronizedMap(lruAcl);
        register(CmsDriverManager.class.getName() + ".accessControlListCache", lruAcl);

        // vfs object cache
        Map<String, Object> vfsObjectCache = new HashMap<String, Object>();
        m_cacheVfsObject = Collections.synchronizedMap(vfsObjectCache);
        register(CmsVfsMemoryObjectCache.class.getName(), vfsObjectCache);

        // memory object cache
        Map<String, Object> memObjectCache = new HashMap<String, Object>();
        m_cacheMemObject = Collections.synchronizedMap(memObjectCache);
        register(CmsMemoryObjectCache.class.getName(), memObjectCache);

        if (LOG.isDebugEnabled()) {
            // this will happen only once during system startup
            LOG.debug(Messages.get().getBundle().key(Messages.LOG_MM_CREATED_1,
                    new Date(System.currentTimeMillis())));
        }
    }

    /**
     * Checks if the property cache is enabled.<p>
     * 
     * @return <code>true</code> if the property cache is enabled
     * 
     * @deprecated use {@link #isEnabled(CacheType)} instead
     */
    @Deprecated
    public boolean isCacheProperty() {

        return isEnabled(CacheType.PROPERTY);
    }

    /**
     * Checks if the property list cache is enabled.<p>
     * 
     * @return <code>true</code> if the property list cache is enabled
     * 
     * @deprecated use {@link #isEnabled(CacheType)} instead
     */
    @Deprecated
    public boolean isCachePropertyList() {

        return isEnabled(CacheType.PROPERTY_LIST);
    }

    /**
     * Checks if the resource cache is enabled.<p>
     * 
     * @return <code>true</code> if the resource cache is enabled
     * 
     * @deprecated use {@link #isEnabled(CacheType)} instead
     */
    @Deprecated
    public boolean isCacheResource() {

        return isEnabled(CacheType.RESOURCE);
    }

    /**
     * Checks if the resource list cache is enabled.<p>
     * 
     * @return <code>true</code> if the resource list cache is enabled
     * 
     * @deprecated use {@link #isEnabled(CacheType)} instead
     */
    @Deprecated
    public boolean isCacheResourceList() {

        return isEnabled(CacheType.RESOURCE_LIST);
    }

    /**
     * Checks if the given cache is enabled.<p>
     * 
     * @param type the cache type to check 
     * 
     * @return <code>true</code> if the given cache is enabled
     */
    public boolean isEnabled(CacheType type) {

        return (m_disabled.get(type) == null);
    }

    /**
     * Checks if there is a registered monitored object with the given key.<p>
     * 
     * @param key the key to look for
     * 
     * @return <code>true</code> if there is a registered monitored object with the given key
     */
    public boolean isMonitoring(String key) {

        return (m_monitoredObjects.get(key) != null);
    }

    /**
     * @see org.opencms.scheduler.I_CmsScheduledJob#launch(CmsObject, Map)
     */
    public String launch(CmsObject cms, Map<String, String> parameters) throws Exception {

        CmsMemoryMonitor monitor = OpenCms.getMemoryMonitor();

        // make sure job is not launched twice
        if (m_currentlyRunning) {
            return null;
        }

        try {
            m_currentlyRunning = true;

            // update the memory status
            monitor.updateStatus();

            // check if the system is in a low memory condition
            if (monitor.lowMemory()) {
                // log warning
                monitor.monitorWriteLog(true);
                // send warning email
                monitor.monitorSendEmail(true);
                // clean up caches     
                monitor.clearCaches();
            }

            // check if regular a log entry must be written
            if ((System.currentTimeMillis() - monitor.m_lastLogStatus) > monitor.m_intervalLog) {
                monitor.monitorWriteLog(false);
            }

            // check if the memory status email must be send
            if ((System.currentTimeMillis() - monitor.m_lastEmailStatus) > monitor.m_intervalEmail) {
                monitor.monitorSendEmail(false);
            }
        } finally {
            // make sure state is reset even if an error occurs, 
            // otherwise MM will not be executed after an error
            m_currentlyRunning = false;
        }

        return null;
    }

    /**
     * Returns true if the system runs low on memory.<p>
     * 
     * @return true if the system runs low on memory
     */
    public boolean lowMemory() {

        return ((m_maxUsagePercent > 0) && (m_memoryCurrent.getUsage() > m_maxUsagePercent));
    }

    /**
     * Adds a new object to the monitor.<p>
     * 
     * @param objectName name of the object
     * @param object the object for monitoring
     */
    public void register(String objectName, Object object) {

        if (enabled()) {
            m_monitoredObjects.put(objectName, object);
        }
    }

    /**
     * Checks if some kind of persistence is required.<p>
     * 
     * This could be overwritten in a distributed environment.<p>
     * 
     * @return <code>true</code> if some kind of persistence is required
     */
    public boolean requiresPersistency() {

        return true;
    }

    /**
     * Sets if the property cache is enabled.<p>
     *
     * @param cacheProperty if the property cache is enabled
     * 
     * @deprecated use {@link #enableCache(CacheType[])} or {@link #disableCache(CacheType[])} instead
     */
    @Deprecated
    public void setCacheProperty(boolean cacheProperty) {

        if (cacheProperty) {
            enableCache(CacheType.PROPERTY);
        } else {
            disableCache(CacheType.PROPERTY);
        }
    }

    /**
     * Sets if the property list cache is enabled.<p>
     *
     * @param cachePropertyList if the property list cache is enabled
     * 
     * @deprecated use {@link #enableCache(CacheType[])} or {@link #disableCache(CacheType[])} instead
     */
    @Deprecated
    public void setCachePropertyList(boolean cachePropertyList) {

        if (cachePropertyList) {
            enableCache(CacheType.PROPERTY_LIST);
        } else {
            disableCache(CacheType.PROPERTY_LIST);
        }
    }

    /**
     * Sets if the resource cache is enabled.<p>
     *
     * @param cacheResource if the resource cache is enabled
     * 
     * @deprecated use {@link #enableCache(CacheType[])} or {@link #disableCache(CacheType[])} instead
     */
    @Deprecated
    public void setCacheResource(boolean cacheResource) {

        if (cacheResource) {
            enableCache(CacheType.RESOURCE);
        } else {
            disableCache(CacheType.RESOURCE);
        }
    }

    /**
     * Sets if the resource list cache is enabled.<p>
     *
     * @param cacheResourceList if the resource list cache is enabled
     * 
     * @deprecated use {@link #enableCache(CacheType[])} or {@link #disableCache(CacheType[])} instead
     */
    @Deprecated
    public void setCacheResourceList(boolean cacheResourceList) {

        if (cacheResourceList) {
            enableCache(CacheType.RESOURCE_LIST);
        } else {
            disableCache(CacheType.RESOURCE_LIST);
        }
    }

    /**
     * Flushes all cached objects.<p>
     * 
     * @throws Exception if something goes wrong 
     */
    public void shutdown() throws Exception {

        for (CacheType type : CacheType.values()) {
            flushCache(type);
        }
    }

    /**
     * Removes the given xml content definition from the cache.<p>
     * 
     * @param key the cache key to remove from cache
     */
    public void uncacheContentDefinition(String key) {

        m_cacheContentDefinitions.remove(key);
    }

    /**
     * Removes the given group from the cache.<p>
     * 
     * The group is removed by name AND also by uuid.<p>
     * 
     * @param group the group to remove from cache
     */
    public void uncacheGroup(CmsGroup group) {

        m_cacheGroup.remove(group.getId().toString());
        m_cacheGroup.remove(group.getName());
    }

    /**
     * Removes the cached lock for the given root path from the cache.<p>
     * 
     * @param rootPath the root path of the lock to remove from cache
     */
    public void uncacheLock(String rootPath) {

        m_cacheLock.remove(rootPath);
    }

    /**
     * Removes the given organizational unit from the cache.<p>
     * 
     * The organizational unit is removed by name AND also by uuid.<p>
     * 
     * @param orgUnit the organizational unit to remove from cache
     */
    public void uncacheOrgUnit(CmsOrganizationalUnit orgUnit) {

        m_cacheOrgUnit.remove(orgUnit.getId().toString());
        m_cacheOrgUnit.remove(orgUnit.getName());
    }

    /**
     * Removes the given project from the cache.<p>
     * 
     * The project is removed by name AND also by uuid.<p>
     * 
     * @param project the project to remove from cache
     */
    public void uncacheProject(CmsProject project) {

        m_cacheProject.remove(project.getUuid().toString());
        m_cacheProject.remove(project.getName());
    }

    /**
     * Removes the given publish job from the cache.<p>
     * 
     * @param publishJob the publish job to remove
     */
    public void uncachePublishJob(CmsPublishJobInfoBean publishJob) {

        m_publishQueue.remove(publishJob);
    }

    /**
     * Removes the given publish job from the history.<p>
     * 
     * @param publishJob the publish job to remove
     */
    public void uncachePublishJobInHistory(CmsPublishJobInfoBean publishJob) {

        m_publishHistory.remove(publishJob);
    }

    /**
     * Removes the given user from the cache.<p>
     * 
     * The user is removed by name AND also by uuid.<p>
     * 
     * @param user the user to remove from cache
     */
    public void uncacheUser(CmsUser user) {

        m_cacheUser.remove(user.getId().toString());
        m_cacheUser.remove(user.getName());
    }

    /**
     * Removes the given vfs object from the cache.<p>
     * 
     * @param key the cache key to remove from cache
     */
    public void uncacheVfsObject(String key) {

        m_cacheVfsObject.remove(key);
    }

    /**
     * Removes the given xml temporary entity from the cache.<p>
     * 
     * @param key the cache key to remove from cache
     */
    public void uncacheXmlTemporaryEntity(String key) {

        m_cacheXmlTemporaryEntity.remove(key);
    }

    /**
     * Clears the OpenCms caches.<p> 
     */
    protected void clearCaches() {

        if ((m_lastClearCache + INTERVAL_CLEAR) > System.currentTimeMillis()) {
            // if the cache has already been cleared less then 15 minutes ago we skip this because 
            // clearing the caches to often will hurt system performance and the 
            // setup seems to be in trouble anyway
            return;
        }
        m_lastClearCache = System.currentTimeMillis();
        if (LOG.isWarnEnabled()) {
            LOG.warn(Messages.get().getBundle().key(Messages.LOG_CLEAR_CACHE_MEM_CONS_0));
        }
        OpenCms.fireCmsEvent(
                new CmsEvent(I_CmsEventListener.EVENT_CLEAR_CACHES, Collections.<String, Object>emptyMap()));
        System.gc();
    }

    /**
     * Returns the cache costs of a monitored object.<p>
     * 
     * <code>obj</code> must be of type {@link CmsLruCache}.<p>
     * 
     * @param obj the object
     * 
     * @return the cache costs or "-"
     */
    protected long getCosts(Object obj) {

        long costs = 0;
        if (obj instanceof CmsLruCache) {
            costs = ((CmsLruCache) obj).getObjectCosts();
            if (costs < 0) {
                costs = 0;
            }
        }

        return costs;
    }

    /**
     * Returns the number of items within a monitored object.<p>
     * 
     * <code>obj</code> must be of type {@link CmsLruCache} or {@link Map}.<p>
     * 
     * @param obj the object
     * 
     * @return the number of items or "-"
     */
    protected String getItems(Object obj) {

        if (obj instanceof CmsLruCache) {
            return Integer.toString(((CmsLruCache) obj).size());
        }
        if (obj instanceof Map) {
            return Integer.toString(((Map<?, ?>) obj).size());
        }
        return "-";
    }

    /**
     * Returns the total size of key strings within a monitored map.<p>
     * 
     * The keys must be of type {@link String}.<p>
     * 
     * @param map the map
     * @param depth the max recursion depth for calculation the size
     * 
     * @return total size of key strings
     */
    protected long getKeySize(Map<?, ?> map, int depth) {

        long keySize = 0;
        try {
            Object[] values = map.values().toArray();
            for (int i = 0, s = values.length; i < s; i++) {

                Object obj = values[i];

                if ((obj instanceof Map) && (depth < MAX_DEPTH)) {
                    keySize += getKeySize((Map<?, ?>) obj, depth + 1);
                    continue;
                }
            }
            values = null;

            Object[] keys = map.keySet().toArray();
            for (int i = 0, s = keys.length; i < s; i++) {

                Object obj = keys[i];

                if (obj instanceof String) {
                    String st = (String) obj;
                    keySize += (st.length() * 2);
                }
            }
        } catch (ConcurrentModificationException e) {
            // this might happen since even the .toArray() method internally creates an iterator
        } catch (Throwable t) {
            // catch all other exceptions otherwise the whole monitor will stop working
            if (LOG.isDebugEnabled()) {
                LOG.debug(Messages.get().getBundle().key(Messages.LOG_CAUGHT_THROWABLE_1, t.getMessage()));
            }
        }

        return keySize;
    }

    /**
     * Returns the total size of key strings within a monitored object.<p>
     * 
     * <code>obj</code> must be of type {@link Map}, the keys must be of type {@link String}.<p>
     * 
     * @param obj the object
     * 
     * @return the total size of key strings
     */
    protected long getKeySize(Object obj) {

        if (obj instanceof Map) {
            return getKeySize((Map<?, ?>) obj, 1);
        }

        return 0;
    }

    /**
     * Returns the max costs for all items within a monitored object.<p>
     * 
     * <code>obj</code> must be of type {@link CmsLruCache} or {@link LRUMap}.<p>
     * 
     * @param obj the object
     * 
     * @return max cost limit or "-"
     */
    protected String getLimit(Object obj) {

        if (obj instanceof CmsLruCache) {
            return Long.toString(((CmsLruCache) obj).getMaxCacheCosts());
        }
        if (obj instanceof LRUMap) {
            return Integer.toString(((LRUMap) obj).maxSize());
        }

        return "-";
    }

    /**
     * Sends a warning or status email with OpenCms Memory information.<p>
     * 
     * @param warning if true, send a memory warning email 
     */
    protected void monitorSendEmail(boolean warning) {

        if ((m_configuration.getEmailSender() == null) || (m_configuration.getEmailReceiver() == null)) {
            // send no mails if not fully configured
            return;
        } else if (warning && (m_warningSendSinceLastStatus && !((m_intervalEmail <= 0)
                && (System.currentTimeMillis() < (m_lastEmailWarning + m_intervalWarning))))) {
            // send no warning email if no status email has been send since the last warning
            // if status is disabled, send no warn email if warn interval has not passed
            return;
        } else if ((!warning) && (m_intervalEmail <= 0)) {
            // if email iterval is <= 0 status email is disabled
            return;
        }
        String date = CmsDateUtil.getDateTimeShort(System.currentTimeMillis());
        String subject;
        String content = "";
        if (warning) {
            m_warningSendSinceLastStatus = true;
            m_lastEmailWarning = System.currentTimeMillis();
            subject = "OpenCms Memory W A R N I N G [" + OpenCms.getSystemInfo().getServerName().toUpperCase() + "/"
                    + date + "]";
            content += "W A R N I N G !\nOpenCms memory consumption on server "
                    + OpenCms.getSystemInfo().getServerName().toUpperCase() + " has reached a critical level !\n\n"
                    + "The configured limit is " + m_maxUsagePercent + "%\n\n";
        } else {
            m_warningSendSinceLastStatus = false;
            m_lastEmailStatus = System.currentTimeMillis();
            subject = "OpenCms Memory Status [" + OpenCms.getSystemInfo().getServerName().toUpperCase() + "/" + date
                    + "]";
        }

        content += "Memory usage report of OpenCms server " + OpenCms.getSystemInfo().getServerName().toUpperCase()
                + " at " + date + "\n\n" + "Memory maximum heap size: " + m_memoryCurrent.getMaxMemory() + " mb\n"
                + "Memory current heap size: " + m_memoryCurrent.getTotalMemory() + " mb\n\n"
                + "Memory currently used   : " + m_memoryCurrent.getUsedMemory() + " mb ("
                + m_memoryCurrent.getUsage() + "%)\n" + "Memory currently unused : "
                + m_memoryCurrent.getFreeMemory() + " mb\n\n\n";

        if (warning) {
            content += "*** Please take action NOW to ensure that no OutOfMemoryException occurs.\n\n\n";
        }

        CmsSessionManager sm = OpenCms.getSessionManager();

        if (sm != null) {
            content += "Current status of the sessions:\n\n";
            content += "Logged in users          : " + sm.getSessionCountAuthenticated() + "\n";
            content += "Currently active sessions: " + sm.getSessionCountCurrent() + "\n";
            content += "Total created sessions   : " + sm.getSessionCountTotal() + "\n\n\n";
        }

        sm = null;

        content += "Current status of the caches:\n\n";
        List<String> keyList = new ArrayList<String>(m_monitoredObjects.keySet());
        Collections.sort(keyList);
        long totalSize = 0;
        for (Iterator<String> keys = keyList.iterator(); keys.hasNext();) {
            String key = keys.next();
            String[] shortKeys = key.split("\\.");
            String shortKey = shortKeys[shortKeys.length - 2] + '.' + shortKeys[shortKeys.length - 1];
            PrintfFormat form = new PrintfFormat("%9s");
            Object obj = m_monitoredObjects.get(key);

            long size = getKeySize(obj) + getValueSize(obj) + getCosts(obj);
            totalSize += size;

            content += new PrintfFormat("%-42.42s").sprintf(shortKey) + "  " + "Entries: "
                    + form.sprintf(getItems(obj)) + "   " + "Limit: " + form.sprintf(getLimit(obj)) + "   "
                    + "Size: " + form.sprintf(Long.toString(size)) + "\n";
        }
        content += "\nTotal size of cache memory monitored: " + totalSize + " (" + (totalSize / 1048576) + ")\n\n";

        String from = m_configuration.getEmailSender();
        List<InternetAddress> receivers = new ArrayList<InternetAddress>();
        List<String> receiverEmails = m_configuration.getEmailReceiver();
        try {
            if ((from != null) && (receiverEmails != null) && !receiverEmails.isEmpty()) {
                Iterator<String> i = receiverEmails.iterator();
                while (i.hasNext()) {
                    receivers.add(new InternetAddress(i.next()));
                }
                CmsSimpleMail email = new CmsSimpleMail();
                email.setFrom(from);
                email.setTo(receivers);
                email.setSubject(subject);
                email.setMsg(content);
                new CmsMailTransport(email).send();
            }
            if (LOG.isInfoEnabled()) {
                if (warning) {
                    LOG.info(Messages.get().getBundle().key(Messages.LOG_MM_WARNING_EMAIL_SENT_0));
                } else {
                    LOG.info(Messages.get().getBundle().key(Messages.LOG_MM_STATUS_EMAIL_SENT_0));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Write a warning or status log entry with OpenCms Memory information.<p>
     * 
     * @param warning if true, write a memory warning log entry 
     */
    protected void monitorWriteLog(boolean warning) {

        if (!LOG.isWarnEnabled()) {
            // we need at last warn level for this output
            return;
        } else if ((!warning) && (!LOG.isInfoEnabled())) {
            // if not warning we need info level
            return;
        } else if (warning && (m_warningLoggedSinceLastStatus && !(((m_intervalLog <= 0)
                && (System.currentTimeMillis() < (m_lastLogWarning + m_intervalWarning)))))) {
            // write no warning log if no status log has been written since the last warning
            // if status is disabled, log no warn entry if warn interval has not passed
            return;
        } else if ((!warning) && (m_intervalLog <= 0)) {
            // if log interval is <= 0 status log is disabled
            return;
        }

        if (warning) {
            m_lastLogWarning = System.currentTimeMillis();
            m_warningLoggedSinceLastStatus = true;
            LOG.warn(Messages.get().getBundle().key(Messages.LOG_MM_WARNING_MEM_CONSUME_2,
                    new Long(m_memoryCurrent.getUsage()), new Integer(m_maxUsagePercent)));
        } else {
            m_warningLoggedSinceLastStatus = false;
            m_lastLogStatus = System.currentTimeMillis();
        }

        if (warning) {
            LOG.warn(Messages.get().getBundle().key(Messages.LOG_MM_WARNING_MEM_STATUS_6,
                    new Object[] { new Long(m_memoryCurrent.getMaxMemory()),
                            new Long(m_memoryCurrent.getTotalMemory()), new Long(m_memoryCurrent.getFreeMemory()),
                            new Long(m_memoryCurrent.getUsedMemory()), new Long(m_memoryCurrent.getUsage()),
                            new Integer(m_maxUsagePercent) }));
        } else {
            m_logCount++;
            LOG.info(Messages.get().getBundle().key(Messages.LOG_MM_LOG_INFO_2,
                    OpenCms.getSystemInfo().getServerName().toUpperCase(), String.valueOf(m_logCount)));

            List<String> keyList = new ArrayList<String>(m_monitoredObjects.keySet());
            Collections.sort(keyList);
            long totalSize = 0;
            for (Iterator<String> keys = keyList.iterator(); keys.hasNext();) {
                String key = keys.next();
                Object obj = m_monitoredObjects.get(key);

                long size = getKeySize(obj) + getValueSize(obj) + getCosts(obj);
                totalSize += size;

                PrintfFormat name1 = new PrintfFormat("%-80s");
                PrintfFormat name2 = new PrintfFormat("%-50s");
                PrintfFormat form = new PrintfFormat("%9s");
                LOG.info(Messages.get().getBundle().key(Messages.LOG_MM_NOWARN_STATUS_5,
                        new Object[] { name1.sprintf(key), name2.sprintf(obj.getClass().getName()),
                                form.sprintf(getItems(obj)), form.sprintf(getLimit(obj)),
                                form.sprintf(Long.toString(size)) }));
            }

            LOG.info(Messages.get().getBundle().key(Messages.LOG_MM_WARNING_MEM_STATUS_6,
                    new Object[] { new Long(m_memoryCurrent.getMaxMemory()),
                            new Long(m_memoryCurrent.getTotalMemory()), new Long(m_memoryCurrent.getFreeMemory()),
                            new Long(m_memoryCurrent.getUsedMemory()), new Long(m_memoryCurrent.getUsage()),
                            new Integer(m_maxUsagePercent), new Long(totalSize), new Long(totalSize / 1048576) })

            );
            LOG.info(Messages.get().getBundle().key(Messages.LOG_MM_WARNING_MEM_STATUS_AVG_6,
                    new Object[] { new Long(m_memoryAverage.getMaxMemory()),
                            new Long(m_memoryAverage.getTotalMemory()), new Long(m_memoryAverage.getFreeMemory()),
                            new Long(m_memoryAverage.getUsedMemory()), new Long(m_memoryAverage.getUsage()),
                            new Integer(m_memoryAverage.getCount()) }));

            CmsSessionManager sm = OpenCms.getSessionManager();

            if (sm != null) {
                LOG.info(Messages.get().getBundle().key(Messages.LOG_MM_SESSION_STAT_3,
                        String.valueOf(sm.getSessionCountAuthenticated()),
                        String.valueOf(sm.getSessionCountCurrent()), String.valueOf(sm.getSessionCountTotal())));
            }
            sm = null;

            for (Iterator<String> i = OpenCms.getSqlManager().getDbPoolUrls().iterator(); i.hasNext();) {
                String poolname = i.next();
                try {
                    LOG.info(Messages.get().getBundle().key(Messages.LOG_MM_CONNECTIONS_3, poolname,
                            Integer.toString(OpenCms.getSqlManager().getActiveConnections(poolname)),
                            Integer.toString(OpenCms.getSqlManager().getIdleConnections(poolname))));
                } catch (Exception exc) {
                    LOG.info(Messages.get().getBundle().key(Messages.LOG_MM_CONNECTIONS_3, poolname,
                            Integer.toString(-1), Integer.toString(-1)));
                }
            }

            LOG.info(Messages.get().getBundle().key(Messages.LOG_MM_STARTUP_TIME_2,
                    CmsDateUtil.getDateTimeShort(OpenCms.getSystemInfo().getStartupTime()),
                    CmsStringUtil.formatRuntime(OpenCms.getSystemInfo().getRuntime())));
        }
    }

    /**
     * Updates the memory information of the memory monitor.<p> 
     */
    protected void updateStatus() {

        m_memoryCurrent.update();
        m_memoryAverage.calculateAverage(m_memoryCurrent);
    }
}