/* * 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); } }