001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.monitor;
029
030import org.opencms.cache.CmsLruCache;
031import org.opencms.cache.CmsMemoryObjectCache;
032import org.opencms.cache.CmsVfsMemoryObjectCache;
033import org.opencms.configuration.CmsSystemConfiguration;
034import org.opencms.db.CmsCacheSettings;
035import org.opencms.db.CmsDriverManager;
036import org.opencms.db.CmsPublishedResource;
037import org.opencms.db.CmsSecurityManager;
038import org.opencms.file.CmsFile;
039import org.opencms.file.CmsGroup;
040import org.opencms.file.CmsObject;
041import org.opencms.file.CmsProject;
042import org.opencms.file.CmsProperty;
043import org.opencms.file.CmsPropertyDefinition;
044import org.opencms.file.CmsResource;
045import org.opencms.file.CmsUser;
046import org.opencms.flex.CmsFlexCache.CmsFlexCacheVariation;
047import org.opencms.i18n.CmsLocaleManager;
048import org.opencms.lock.CmsLock;
049import org.opencms.lock.CmsLockManager;
050import org.opencms.mail.CmsMailTransport;
051import org.opencms.mail.CmsSimpleMail;
052import org.opencms.main.CmsEvent;
053import org.opencms.main.CmsLog;
054import org.opencms.main.CmsSessionManager;
055import org.opencms.main.I_CmsEventListener;
056import org.opencms.main.OpenCms;
057import org.opencms.publish.CmsPublishHistory;
058import org.opencms.publish.CmsPublishJobInfoBean;
059import org.opencms.publish.CmsPublishQueue;
060import org.opencms.scheduler.I_CmsScheduledJob;
061import org.opencms.security.CmsAccessControlList;
062import org.opencms.security.CmsOrganizationalUnit;
063import org.opencms.security.CmsPermissionSet;
064import org.opencms.security.CmsRole;
065import org.opencms.security.I_CmsPermissionHandler;
066import org.opencms.util.CmsDateUtil;
067import org.opencms.util.CmsStringUtil;
068import org.opencms.util.CmsUUID;
069import org.opencms.util.PrintfFormat;
070import org.opencms.xml.CmsXmlContentDefinition;
071import org.opencms.xml.CmsXmlEntityResolver;
072
073import java.util.ArrayList;
074import java.util.Collections;
075import java.util.ConcurrentModificationException;
076import java.util.Date;
077import java.util.HashMap;
078import java.util.Iterator;
079import java.util.List;
080import java.util.Locale;
081import java.util.Map;
082import java.util.concurrent.ConcurrentHashMap;
083
084import javax.mail.internet.InternetAddress;
085
086import org.apache.commons.collections.Buffer;
087import org.apache.commons.collections.buffer.SynchronizedBuffer;
088import org.apache.commons.collections.map.LRUMap;
089import org.apache.commons.logging.Log;
090
091import com.google.common.cache.CacheBuilder;
092
093/**
094 * Monitors OpenCms memory consumption.<p>
095 *
096 * The memory monitor also provides all kind of caches used in the OpenCms core.<p>
097 *
098 * @since 6.0.0
099 */
100public class CmsMemoryMonitor implements I_CmsScheduledJob {
101
102    /** Cache types. */
103    public enum CacheType {
104        /** Access Control Lists cache. */
105        ACL,
106        /** Content Definition cache. */
107        CONTENT_DEFINITION,
108        /** Group cache. */
109        GROUP,
110        /** Has Role cache. */
111        HAS_ROLE,
112        /** Locale cache. */
113        LOCALE,
114        /** Lock cache. */
115        LOCK,
116        /** Memory Object cache. */
117        MEMORY_OBJECT,
118        /** Organizational Unit cache. */
119        ORG_UNIT,
120        /** Permission cache. */
121        PERMISSION,
122        /** Offline Project cache. */
123        PROJECT,
124        /** Project resources cache. */
125        PROJECT_RESOURCES,
126        /** Property cache. */
127        PROPERTY,
128        /** Property List cache. */
129        PROPERTY_LIST,
130        /** Publish history cache. */
131        PUBLISH_HISTORY,
132        /** Publish queue cache. */
133        PUBLISH_QUEUE,
134        /** Published resources cache. */
135        PUBLISHED_RESOURCES,
136        /** Resource cache. */
137        RESOURCE,
138        /** Resource List cache. */
139        RESOURCE_LIST,
140        /** Role List cache. */
141        ROLE_LIST,
142        /** User cache. */
143        USER,
144        /** User list cache. */
145        USER_LIST,
146        /** User Groups cache. */
147        USERGROUPS,
148        /** VFS Object cache. */
149        VFS_OBJECT,
150        /** XML Entity Permanent cache. */
151        XML_ENTITY_PERM,
152        /** XML Entity Temporary cache. */
153        XML_ENTITY_TEMP;
154    }
155
156    /** The concurrency level for the guava caches. */
157    private static final int CONCURRENCY_LEVEL = 8;
158
159    /** Set interval for clearing the caches to 10 minutes. */
160    private static final int INTERVAL_CLEAR = 1000 * 60 * 10;
161
162    /** The log object for this class. */
163    private static final Log LOG = CmsLog.getLog(CmsMemoryMonitor.class);
164
165    /** Flag indicating if monitor is currently running. */
166    private static boolean m_currentlyRunning;
167
168    /** Maximum depth for object size recursion. */
169    private static final int MAX_DEPTH = 5;
170
171    /** Cache for access control lists. */
172    private Map<String, CmsAccessControlList> m_cacheAccessControlList;
173
174    /** A temporary cache for XML content definitions. */
175    private Map<String, CmsXmlContentDefinition> m_cacheContentDefinitions;
176
177    /** Cache for groups. */
178    private Map<String, CmsGroup> m_cacheGroup;
179
180    /** Cache for roles. */
181    private Map<String, Boolean> m_cacheHasRoles;
182
183    /** A cache for accelerated locale lookup. */
184    private Map<String, Locale> m_cacheLocale;
185
186    /** Cache for the resource locks. */
187    private Map<String, CmsLock> m_cacheLock;
188
189    /** The memory object cache map. */
190    private Map<String, Object> m_cacheMemObject;
191
192    /** Cache for organizational units. */
193    private Map<String, CmsOrganizationalUnit> m_cacheOrgUnit;
194
195    /** Cache for permission checks. */
196    private Map<String, I_CmsPermissionHandler.CmsPermissionCheckResult> m_cachePermission;
197
198    /** Cache for offline projects. */
199    private Map<String, CmsProject> m_cacheProject;
200
201    /** Cache for project resources. */
202    private Map<String, List<CmsResource>> m_cacheProjectResources;
203
204    /** Cache for properties. */
205    private Map<String, CmsProperty> m_cacheProperty;
206
207    /** Cache for property lists. */
208    private Map<String, List<CmsProperty>> m_cachePropertyList;
209
210    /** Cache for published resources. */
211    private Map<String, List<CmsPublishedResource>> m_cachePublishedResources;
212
213    /** Cache for resources. */
214    private Map<String, CmsResource> m_cacheResource;
215
216    /** Cache for resource lists. */
217    private Map<String, List<CmsResource>> m_cacheResourceList;
218
219    /** Cache for role lists. */
220    private Map<String, List<CmsRole>> m_cacheRoleLists;
221
222    /** Cache for user data. */
223    private Map<String, CmsUser> m_cacheUser;
224
225    /** Cache for user groups. */
226    private Map<String, List<CmsGroup>> m_cacheUserGroups;
227
228    /** Cache for user lists. */
229    private Map<String, List<CmsUser>> m_cacheUserList;
230
231    /** The vfs memory cache map. */
232    private Map<String, Object> m_cacheVfsObject;
233
234    /** A permanent cache to avoid multiple readings of often used files from the VFS. */
235    private Map<String, byte[]> m_cacheXmlPermanentEntity;
236
237    /** A temporary cache to avoid multiple readings of often used files from the VFS. */
238    private Map<String, byte[]> m_cacheXmlTemporaryEntity;
239
240    /** The memory monitor configuration. */
241    private CmsMemoryMonitorConfiguration m_configuration;
242
243    /** Map to keep track of disabled caches. */
244    private Map<CacheType, Boolean> m_disabled = new HashMap<CacheType, Boolean>();
245
246    /** Interval in which emails are send. */
247    private int m_intervalEmail;
248
249    /** Interval in which the log is written. */
250    private int m_intervalLog;
251
252    /** Interval between 2 warnings. */
253    private int m_intervalWarning;
254
255    /** The time the caches were last cleared. */
256    private long m_lastClearCache;
257
258    /** The time the last status email was send. */
259    private long m_lastEmailStatus;
260
261    /** The time the last warning email was send. */
262    private long m_lastEmailWarning;
263
264    /** The time the last status log was written. */
265    private long m_lastLogStatus;
266
267    /** The time the last warning log was written. */
268    private long m_lastLogWarning;
269
270    /** The number of times the log entry was written. */
271    private int m_logCount;
272
273    /** Memory percentage to reach to go to warning level. */
274    private int m_maxUsagePercent;
275
276    /** The average memory status. */
277    private CmsMemoryStatus m_memoryAverage;
278
279    /** The current memory status. */
280    private CmsMemoryStatus m_memoryCurrent;
281
282    /** Contains the object to be monitored. */
283    private Map<String, Object> m_monitoredObjects;
284
285    /** Buffer for publish history. */
286    private Buffer m_publishHistory;
287
288    /** Buffer for publish jobs. */
289    private Buffer m_publishQueue;
290
291    /** Flag for memory warning mail send. */
292    private boolean m_warningLoggedSinceLastStatus;
293
294    /** Flag for memory warning mail send. */
295    private boolean m_warningSendSinceLastStatus;
296
297    /**
298     * Empty constructor, required by OpenCms scheduler.<p>
299     */
300    public CmsMemoryMonitor() {
301
302        m_monitoredObjects = new HashMap<String, Object>();
303    }
304
305    /**
306     * Creates a thread safe LRU cache map based on the guava cache builder.<p>
307     * Use this instead of synchronized maps for better performance.<p>
308     *
309     * @param capacity the cache capacity
310     *
311     * @return the cache map
312     */
313    @SuppressWarnings("unchecked")
314    public static <T, V> Map<T, V> createLRUCacheMap(int capacity) {
315
316        CacheBuilder<?, ?> builder = CacheBuilder.newBuilder().concurrencyLevel(CONCURRENCY_LEVEL).maximumSize(
317            capacity);
318        builder.build();
319        return (Map<T, V>)(builder.build().asMap());
320    }
321
322    /**
323     * Returns the size of objects that are instances of
324     * <code>byte[]</code>, <code>String</code>, <code>CmsFile</code>,<code>I_CmsLruCacheObject</code>.<p>
325     * For other objects, a size of 0 is returned.
326     *
327     * @param obj the object
328     * @return the size of the object
329     */
330    public static int getMemorySize(Object obj) {
331
332        if (obj instanceof I_CmsMemoryMonitorable) {
333            return ((I_CmsMemoryMonitorable)obj).getMemorySize();
334        }
335
336        if (obj instanceof byte[]) {
337            // will always be a total of 16 + 8
338            return 8 + (int)(Math.ceil(((byte[])obj).length / 16.0) * 16.0);
339        }
340
341        if (obj instanceof String) {
342            // will always be a total of 16 + 24
343            return 24 + (int)(Math.ceil(((String)obj).length() / 8.0) * 16.0);
344        }
345
346        if (obj instanceof CmsFile) {
347            CmsFile f = (CmsFile)obj;
348            if (f.getContents() != null) {
349                return f.getContents().length + 1024;
350            } else {
351                return 1024;
352            }
353        }
354
355        if (obj instanceof CmsUUID) {
356            return 184; // worst case if UUID String has been generated
357        }
358
359        if (obj instanceof CmsPermissionSet) {
360            return 16; // two ints
361        }
362
363        if (obj instanceof CmsResource) {
364            return 1024; // estimated size
365        }
366
367        if (obj instanceof CmsPublishedResource) {
368            return 512; // estimated size
369        }
370
371        if (obj instanceof CmsUser) {
372            return 2048; // estimated size
373        }
374
375        if (obj instanceof CmsGroup) {
376            return 512; // estimated size
377        }
378
379        if (obj instanceof CmsProject) {
380            return 512; // estimated size
381        }
382
383        if (obj instanceof Boolean) {
384            return 8; // one boolean
385        }
386
387        if (obj instanceof CmsProperty) {
388            int size = 8;
389
390            CmsProperty property = (CmsProperty)obj;
391            size += getMemorySize(property.getName());
392
393            if (property.getResourceValue() != null) {
394                size += getMemorySize(property.getResourceValue());
395            }
396
397            if (property.getStructureValue() != null) {
398                size += getMemorySize(property.getStructureValue());
399            }
400
401            if (property.getOrigin() != null) {
402                size += getMemorySize(property.getOrigin());
403            }
404
405            return size;
406        }
407
408        if (obj instanceof CmsPropertyDefinition) {
409            int size = 8;
410
411            CmsPropertyDefinition propDef = (CmsPropertyDefinition)obj;
412            size += getMemorySize(propDef.getName());
413            size += getMemorySize(propDef.getId());
414
415            return size;
416        }
417
418        return 8;
419    }
420
421    /**
422     * Returns the total value size of a list object.<p>
423     *
424     * @param listValue the list object
425     * @param depth the max recursion depth for calculation the size
426     *
427     * @return the size of the list object
428     */
429    public static long getValueSize(List<?> listValue, int depth) {
430
431        long totalSize = 0;
432        try {
433            Object[] values = listValue.toArray();
434            for (int i = 0, s = values.length; i < s; i++) {
435
436                Object obj = values[i];
437
438                if (obj instanceof CmsAccessControlList) {
439                    obj = ((CmsAccessControlList)obj).getPermissionMap();
440                }
441
442                if (obj instanceof CmsFlexCacheVariation) {
443                    obj = ((CmsFlexCacheVariation)obj).m_map;
444                }
445
446                if ((obj instanceof Map) && (depth < MAX_DEPTH)) {
447                    totalSize += getValueSize((Map<?, ?>)obj, depth + 1);
448                    continue;
449                }
450
451                if ((obj instanceof List) && (depth < MAX_DEPTH)) {
452                    totalSize += getValueSize((List<?>)obj, depth + 1);
453                    continue;
454                }
455
456                totalSize += getMemorySize(obj);
457            }
458        } catch (ConcurrentModificationException e) {
459            // this might happen since even the .toArray() method internally creates an iterator
460        } catch (Throwable t) {
461            // catch all other exceptions otherwise the whole monitor will stop working
462            if (LOG.isDebugEnabled()) {
463                LOG.debug(Messages.get().getBundle().key(Messages.LOG_CAUGHT_THROWABLE_1, t.getMessage()));
464            }
465        }
466
467        return totalSize;
468    }
469
470    /**
471     * Returns the total value size of a map object.<p>
472     *
473     * @param mapValue the map object
474     * @param depth the max recursion depth for calculation the size
475     *
476     * @return the size of the map object
477     */
478    public static long getValueSize(Map<?, ?> mapValue, int depth) {
479
480        long totalSize = 0;
481        try {
482            Object[] values = mapValue.values().toArray();
483            for (int i = 0, s = values.length; i < s; i++) {
484
485                Object obj = values[i];
486
487                if (obj instanceof CmsAccessControlList) {
488                    obj = ((CmsAccessControlList)obj).getPermissionMap();
489                }
490
491                if (obj instanceof CmsFlexCacheVariation) {
492                    obj = ((CmsFlexCacheVariation)obj).m_map;
493                }
494
495                if ((obj instanceof Map) && (depth < MAX_DEPTH)) {
496                    totalSize += getValueSize((Map<?, ?>)obj, depth + 1);
497                    continue;
498                }
499
500                if ((obj instanceof List) && (depth < MAX_DEPTH)) {
501                    totalSize += getValueSize((List<?>)obj, depth + 1);
502                    continue;
503                }
504
505                totalSize += getMemorySize(obj);
506            }
507        } catch (ConcurrentModificationException e) {
508            // this might happen since even the .toArray() method internally creates an iterator
509        } catch (Throwable t) {
510            // catch all other exceptions otherwise the whole monitor will stop working
511            if (LOG.isDebugEnabled()) {
512                LOG.debug(Messages.get().getBundle().key(Messages.LOG_CAUGHT_THROWABLE_1, t.getMessage()));
513            }
514        }
515
516        return totalSize;
517    }
518
519    /**
520     * Returns the value sizes of value objects within the monitored object.<p>
521     *
522     * @param obj the object
523     *
524     * @return the value sizes of value objects or "-"-fields
525     */
526    public static long getValueSize(Object obj) {
527
528        if (obj instanceof CmsLruCache) {
529            return ((CmsLruCache)obj).size();
530        }
531
532        if (obj instanceof Map) {
533            return getValueSize((Map<?, ?>)obj, 1);
534        }
535
536        if (obj instanceof List) {
537            return getValueSize((List<?>)obj, 1);
538        }
539
540        try {
541            return getMemorySize(obj);
542        } catch (Exception exc) {
543            return 0;
544        }
545    }
546
547    /**
548     * Caches the given acl under the given cache key.<p>
549     *
550     * @param key the cache key
551     * @param acl the acl to cache
552     */
553    public void cacheACL(String key, CmsAccessControlList acl) {
554
555        if (m_disabled.get(CacheType.ACL) != null) {
556            return;
557        }
558        m_cacheAccessControlList.put(key, acl);
559    }
560
561    /**
562     * Caches the given content definition under the given cache key.<p>
563     *
564     * @param key the cache key
565     * @param contentDefinition the content definition to cache
566     */
567    public void cacheContentDefinition(String key, CmsXmlContentDefinition contentDefinition) {
568
569        if (m_disabled.get(CacheType.CONTENT_DEFINITION) != null) {
570            return;
571        }
572        m_cacheContentDefinitions.put(key, contentDefinition);
573    }
574
575    /**
576     * Caches the given group under its id AND fully qualified name.<p>
577     *
578     * @param group the group to cache
579     */
580    public void cacheGroup(CmsGroup group) {
581
582        if (m_disabled.get(CacheType.GROUP) != null) {
583            return;
584        }
585        m_cacheGroup.put(group.getId().toString(), group);
586        m_cacheGroup.put(group.getName(), group);
587    }
588
589    /**
590     * Caches the given locale under the given cache key.<p>
591     *
592     * @param key the cache key
593     * @param locale the locale to cache
594     */
595    public void cacheLocale(String key, Locale locale) {
596
597        if (m_cacheLocale != null) {
598            if (m_disabled.get(CacheType.LOCALE) != null) {
599                return;
600            }
601            // this may be accessed before initialization
602            m_cacheLocale.put(key, locale);
603        }
604    }
605
606    /**
607     * Caches the given lock.<p>
608     *
609     * The lock is cached by it resource's root path.<p>
610     *
611     * @param lock the lock to cache
612     */
613    public void cacheLock(CmsLock lock) {
614
615        if (m_disabled.get(CacheType.LOCK) != null) {
616            return;
617        }
618        m_cacheLock.put(lock.getResourceName(), lock);
619    }
620
621    /**
622     * Caches the given object under the given cache key.<p>
623     *
624     * @param key the cache key
625     * @param obj the object to cache
626     */
627    public void cacheMemObject(String key, Object obj) {
628
629        if (m_disabled.get(CacheType.MEMORY_OBJECT) != null) {
630            return;
631        }
632        m_cacheMemObject.put(key, obj);
633    }
634
635    /**
636     * Caches the given organizational under its id AND the fully qualified name.<p>
637     *
638     * @param orgUnit the organizational unit to cache
639     */
640    public void cacheOrgUnit(CmsOrganizationalUnit orgUnit) {
641
642        if (m_disabled.get(CacheType.ORG_UNIT) != null) {
643            return;
644        }
645        m_cacheOrgUnit.put(orgUnit.getId().toString(), orgUnit);
646        m_cacheOrgUnit.put(orgUnit.getName(), orgUnit);
647    }
648
649    /**
650     * Caches the given permission check result under the given cache key.<p>
651     *
652     * @param key the cache key
653     * @param permission the permission check result to cache
654     */
655    public void cachePermission(String key, I_CmsPermissionHandler.CmsPermissionCheckResult permission) {
656
657        if (m_disabled.get(CacheType.PERMISSION) != null) {
658            return;
659        }
660        m_cachePermission.put(key, permission);
661    }
662
663    /**
664     * Caches the given project under its id AND the fully qualified name.<p>
665     *
666     * @param project the project to cache
667     */
668    public void cacheProject(CmsProject project) {
669
670        if (m_disabled.get(CacheType.PROJECT) != null) {
671            return;
672        }
673        m_cacheProject.put(project.getUuid().toString(), project);
674        m_cacheProject.put(project.getName(), project);
675    }
676
677    /**
678     * Caches the given project resource list under the given cache key.<p>
679     *
680     * @param key the cache key
681     * @param projectResources the project resources to cache
682     */
683    public void cacheProjectResources(String key, List<CmsResource> projectResources) {
684
685        if (m_disabled.get(CacheType.PROJECT_RESOURCES) != null) {
686            return;
687        }
688        m_cacheProjectResources.put(key, projectResources);
689    }
690
691    /**
692     * Caches the given property under the given cache key.<p>
693     *
694     * @param key the cache key
695     * @param property the property to cache
696     */
697    public void cacheProperty(String key, CmsProperty property) {
698
699        if (m_disabled.get(CacheType.PROPERTY) != null) {
700            return;
701        }
702        m_cacheProperty.put(key, property);
703    }
704
705    /**
706     * Caches the given property list under the given cache key.<p>
707     *
708     * @param key the cache key
709     * @param propertyList the property list to cache
710     */
711    public void cachePropertyList(String key, List<CmsProperty> propertyList) {
712
713        if (m_disabled.get(CacheType.PROPERTY_LIST) != null) {
714            return;
715        }
716        m_cachePropertyList.put(key, propertyList);
717    }
718
719    /**
720     * Caches the given published resources list under the given cache key.<p>
721     *
722     * @param cacheKey the cache key
723     * @param publishedResources the published resources list to cache
724     */
725    public void cachePublishedResources(String cacheKey, List<CmsPublishedResource> publishedResources) {
726
727        if (m_disabled.get(CacheType.PUBLISHED_RESOURCES) != null) {
728            return;
729        }
730        m_cachePublishedResources.put(cacheKey, publishedResources);
731    }
732
733    /**
734     * Caches the given publish job.<p>
735     *
736     * @param publishJob the publish job
737     */
738    @SuppressWarnings("unchecked")
739    public void cachePublishJob(CmsPublishJobInfoBean publishJob) {
740
741        if (m_disabled.get(CacheType.PUBLISH_QUEUE) != null) {
742            return;
743        }
744        m_publishQueue.add(publishJob);
745    }
746
747    /**
748     * Caches the given publish job in the publish job history.<p>
749     *
750     * @param publishJob the publish job
751     */
752    @SuppressWarnings("unchecked")
753    public void cachePublishJobInHistory(CmsPublishJobInfoBean publishJob) {
754
755        if (m_disabled.get(CacheType.PUBLISH_HISTORY) != null) {
756            return;
757        }
758        m_publishHistory.add(publishJob);
759    }
760
761    /**
762     * Caches the given resource under the given cache key.<p>
763     *
764     * @param key the cache key
765     * @param resource the resource to cache
766     */
767    public void cacheResource(String key, CmsResource resource) {
768
769        if (m_disabled.get(CacheType.RESOURCE) != null) {
770            return;
771        }
772        m_cacheResource.put(key, resource);
773    }
774
775    /**
776     * Caches the given resource list under the given cache key.<p>
777     *
778     * @param key the cache key
779     * @param resourceList the resource list to cache
780     */
781    public void cacheResourceList(String key, List<CmsResource> resourceList) {
782
783        if (m_disabled.get(CacheType.RESOURCE_LIST) != null) {
784            return;
785        }
786        m_cacheResourceList.put(key, resourceList);
787    }
788
789    /**
790     * Caches the given value under the given cache key.<p>
791     *
792     * @param key the cache key
793     * @param hasRole if the user has the given role
794     */
795    public void cacheRole(String key, boolean hasRole) {
796
797        if (m_disabled.get(CacheType.HAS_ROLE) != null) {
798            return;
799        }
800        m_cacheHasRoles.put(key, Boolean.valueOf(hasRole));
801    }
802
803    /**
804     * Caches the given value under the given cache key.<p>
805     *
806     * @param key the cache key
807     * @param roles the roles of the user
808     */
809    public void cacheRoleList(String key, List<CmsRole> roles) {
810
811        if (m_disabled.get(CacheType.ROLE_LIST) != null) {
812            return;
813        }
814        m_cacheRoleLists.put(key, roles);
815    }
816
817    /**
818     * Caches the given user under its id AND the fully qualified name.<p>
819     *
820     * @param user the user to cache
821     */
822    public void cacheUser(CmsUser user) {
823
824        if (m_disabled.get(CacheType.USER) != null) {
825            return;
826        }
827        m_cacheUser.put(user.getId().toString(), user);
828        m_cacheUser.put(user.getName(), user);
829    }
830
831    /**
832     * Caches the given list of user groups under the given cache key.<p>
833     *
834     * @param key the cache key
835     * @param userGroups the list of user groups to cache
836     */
837    public void cacheUserGroups(String key, List<CmsGroup> userGroups) {
838
839        if (m_disabled.get(CacheType.USERGROUPS) != null) {
840            return;
841        }
842        m_cacheUserGroups.put(key, userGroups);
843    }
844
845    /**
846     * Caches the given list of users under the given cache key.<p>
847     *
848     * @param key the cache key
849     * @param userList the list of users to cache
850     */
851    public void cacheUserList(String key, List<CmsUser> userList) {
852
853        if (m_disabled.get(CacheType.USER_LIST) != null) {
854            return;
855        }
856        m_cacheUserList.put(key, userList);
857    }
858
859    /**
860     * Caches the given vfs object under the given cache key.<p>
861     *
862     * @param key the cache key
863     * @param obj the vfs object to cache
864     */
865    public void cacheVfsObject(String key, Object obj) {
866
867        if (m_disabled.get(CacheType.VFS_OBJECT) != null) {
868            return;
869        }
870        m_cacheVfsObject.put(key, obj);
871    }
872
873    /**
874     * Caches the given xml entity under the given system id.<p>
875     *
876     * @param systemId the cache key
877     * @param content the content to cache
878     */
879    public void cacheXmlPermanentEntity(String systemId, byte[] content) {
880
881        if (m_disabled.get(CacheType.XML_ENTITY_PERM) != null) {
882            return;
883        }
884        m_cacheXmlPermanentEntity.put(systemId, content);
885    }
886
887    /**
888     * Caches the given xml entity under the given cache key.<p>
889     *
890     * @param key the cache key
891     * @param content the content to cache
892     */
893    public void cacheXmlTemporaryEntity(String key, byte[] content) {
894
895        if (m_disabled.get(CacheType.XML_ENTITY_TEMP) != null) {
896            return;
897        }
898        m_cacheXmlTemporaryEntity.put(key, content);
899    }
900
901    /**
902     * Clears the access control list cache when access control entries are changed.<p>
903     */
904    public void clearAccessControlListCache() {
905
906        flushCache(CacheType.ACL);
907        flushCache(CacheType.PERMISSION);
908        clearResourceCache();
909    }
910
911    /**
912     * Clears almost all internal caches.<p>
913     */
914    public void clearCache() {
915
916        clearPrincipalsCache();
917
918        flushCache(CacheType.PROJECT);
919        flushCache(CacheType.RESOURCE);
920        flushCache(CacheType.RESOURCE_LIST);
921        flushCache(CacheType.PROPERTY);
922        flushCache(CacheType.PROPERTY_LIST);
923        flushCache(CacheType.PROJECT_RESOURCES);
924        flushCache(CacheType.PUBLISHED_RESOURCES);
925    }
926
927    /**
928     * Clears all internal principal-related caches.<p>
929     */
930    public void clearPrincipalsCache() {
931
932        flushCache(CacheType.USER);
933        flushCache(CacheType.GROUP);
934        flushCache(CacheType.ORG_UNIT);
935        flushCache(CacheType.ACL);
936        flushCache(CacheType.PERMISSION);
937        flushCache(CacheType.HAS_ROLE);
938        flushCache(CacheType.ROLE_LIST);
939        flushCache(CacheType.USERGROUPS);
940        flushCache(CacheType.USER_LIST);
941    }
942
943    /**
944     * Clears all the depending caches when a resource was changed.<p>
945     */
946    public void clearResourceCache() {
947
948        flushCache(CacheType.RESOURCE);
949        flushCache(CacheType.RESOURCE_LIST);
950        flushCache(CacheType.HAS_ROLE);
951        flushCache(CacheType.ROLE_LIST);
952    }
953
954    /**
955     * Clears the user cache for the given user.<p>
956     *
957     * @param user the user
958     */
959    public void clearUserCache(CmsUser user) {
960
961        uncacheUser(user);
962        flushCache(CacheType.RESOURCE_LIST);
963    }
964
965    /**
966     * Disables the given cache.<p>
967     *
968     * @param types the cache type to disable
969     */
970    public void disableCache(CacheType... types) {
971
972        for (CacheType type : types) {
973            m_disabled.put(type, Boolean.TRUE);
974        }
975        flushCache(types);
976    }
977
978    /**
979     * Enables the given cache.<p>
980     *
981     * @param types the cache type to disable
982     */
983    public void enableCache(CacheType... types) {
984
985        for (CacheType type : types) {
986            m_disabled.remove(type);
987        }
988    }
989
990    /**
991     * Returns if monitoring is enabled.<p>
992     *
993     * @return true if monitoring is enabled
994     */
995    public boolean enabled() {
996
997        return true;
998    }
999
1000    /**
1001     * Flushes the ACL cache.<p>
1002     *
1003     * @deprecated use {@link #flushCache(CacheType[])} instead
1004     */
1005    @Deprecated
1006    public void flushACLs() {
1007
1008        flushCache(CacheType.ACL);
1009    }
1010
1011    /**
1012     * Flushes the given cache.<p>
1013     *
1014     * @param types the cache types to flush
1015     */
1016    public void flushCache(CacheType... types) {
1017
1018        for (CacheType type : types) {
1019            switch (type) {
1020                case ACL:
1021                    m_cacheAccessControlList.clear();
1022                    break;
1023                case CONTENT_DEFINITION:
1024                    m_cacheContentDefinitions.clear();
1025                    break;
1026                case GROUP:
1027                    m_cacheGroup.clear();
1028                    break;
1029                case HAS_ROLE:
1030                    m_cacheHasRoles.clear();
1031                    break;
1032                case LOCALE:
1033                    m_cacheLocale.clear();
1034                    break;
1035                case LOCK:
1036                    m_cacheLock.clear();
1037                    break;
1038                case MEMORY_OBJECT:
1039                    m_cacheMemObject.clear();
1040                    break;
1041                case ORG_UNIT:
1042                    m_cacheOrgUnit.clear();
1043                    break;
1044                case PERMISSION:
1045                    m_cachePermission.clear();
1046                    break;
1047                case PROJECT:
1048                    m_cacheProject.clear();
1049                    break;
1050                case PROJECT_RESOURCES:
1051                    m_cacheProjectResources.clear();
1052                    break;
1053                case PROPERTY:
1054                    m_cacheProperty.clear();
1055                    break;
1056                case PROPERTY_LIST:
1057                    m_cachePropertyList.clear();
1058                    break;
1059                case PUBLISHED_RESOURCES:
1060                    m_cachePublishedResources.clear();
1061                    break;
1062                case PUBLISH_HISTORY:
1063                    m_publishHistory.clear();
1064                    break;
1065                case PUBLISH_QUEUE:
1066                    m_publishQueue.clear();
1067                    break;
1068                case RESOURCE:
1069                    m_cacheResource.clear();
1070                    break;
1071                case RESOURCE_LIST:
1072                    m_cacheResourceList.clear();
1073                    break;
1074                case ROLE_LIST:
1075                    m_cacheRoleLists.clear();
1076                    break;
1077                case USER:
1078                    m_cacheUser.clear();
1079                    break;
1080                case USERGROUPS:
1081                    m_cacheUserGroups.clear();
1082                    break;
1083                case USER_LIST:
1084                    m_cacheUserList.clear();
1085                    break;
1086                case VFS_OBJECT:
1087                    m_cacheVfsObject.clear();
1088                    break;
1089                case XML_ENTITY_PERM:
1090                    m_cacheXmlPermanentEntity.clear();
1091                    break;
1092                case XML_ENTITY_TEMP:
1093                    m_cacheXmlTemporaryEntity.clear();
1094                    break;
1095                default:
1096                    // can't happen
1097            }
1098        }
1099    }
1100
1101    /**
1102     * Flushes the xml content definitions cache.<p>
1103     *
1104     * @deprecated use {@link #flushCache(CacheType[])} instead
1105     */
1106    @Deprecated
1107    public void flushContentDefinitions() {
1108
1109        flushCache(CacheType.CONTENT_DEFINITION);
1110    }
1111
1112    /**
1113     * Flushes the group cache.<p>
1114     *
1115     * @deprecated use {@link #flushCache(CacheType[])} instead
1116     */
1117    @Deprecated
1118    public void flushGroups() {
1119
1120        flushCache(CacheType.GROUP);
1121    }
1122
1123    /**
1124     * Flushes the locale cache.<p>
1125     *
1126     * @deprecated use {@link #flushCache(CacheType[])} instead
1127     */
1128    @Deprecated
1129    public void flushLocales() {
1130
1131        flushCache(CacheType.LOCALE);
1132    }
1133
1134    /**
1135     * Flushes the locks cache.<p>
1136     *
1137     * @param newLocks if not <code>null</code> the lock cache is replaced by the given map
1138     */
1139    public void flushLocks(Map<String, CmsLock> newLocks) {
1140
1141        if ((newLocks == null) || newLocks.isEmpty()) {
1142            flushCache(CacheType.LOCK);
1143            return;
1144        }
1145        // initialize new lock cache
1146        Map<String, CmsLock> newLockCache = new ConcurrentHashMap<String, CmsLock>(newLocks);
1147        // register it
1148        register(CmsLockManager.class.getName(), newLockCache);
1149        // save the old cache
1150        Map<String, CmsLock> oldCache = m_cacheLock;
1151        // replace the old by the new cache
1152        m_cacheLock = newLockCache;
1153        // clean up the old cache
1154        oldCache.clear();
1155    }
1156
1157    /**
1158     * Flushes the memory object cache.<p>
1159     *
1160     * @deprecated use {@link #flushCache(CacheType[])} instead
1161     */
1162    @Deprecated
1163    public void flushMemObjects() {
1164
1165        flushCache(CacheType.MEMORY_OBJECT);
1166    }
1167
1168    /**
1169     * Flushes the organizational unit cache.<p>
1170     *
1171     * @deprecated use {@link #flushCache(CacheType[])} instead
1172     */
1173    @Deprecated
1174    public void flushOrgUnits() {
1175
1176        flushCache(CacheType.ORG_UNIT);
1177    }
1178
1179    /**
1180     * Flushes the permission check result cache.<p>
1181     *
1182     * @deprecated use {@link #flushCache(CacheType[])} instead
1183     */
1184    @Deprecated
1185    public void flushPermissions() {
1186
1187        flushCache(CacheType.PERMISSION);
1188    }
1189
1190    /**
1191     * Flushes the project resources cache.<p>
1192     *
1193     * @deprecated use {@link #flushCache(CacheType[])} instead
1194     */
1195    @Deprecated
1196    public void flushProjectResources() {
1197
1198        flushCache(CacheType.PROJECT_RESOURCES);
1199    }
1200
1201    /**
1202     * Flushes the project cache.<p>
1203     *
1204     * @deprecated use {@link #flushCache(CacheType[])} instead
1205     */
1206    @Deprecated
1207    public void flushProjects() {
1208
1209        flushCache(CacheType.PROJECT);
1210    }
1211
1212    /**
1213     * Flushes the property cache.<p>
1214     *
1215     * @deprecated use {@link #flushCache(CacheType[])} instead
1216     */
1217    @Deprecated
1218    public void flushProperties() {
1219
1220        flushCache(CacheType.PROPERTY);
1221    }
1222
1223    /**
1224     * Flushes the property list cache.<p>
1225     *
1226     * @deprecated use {@link #flushCache(CacheType[])} instead
1227     */
1228    @Deprecated
1229    public void flushPropertyLists() {
1230
1231        flushCache(CacheType.PROPERTY_LIST);
1232    }
1233
1234    /**
1235     * Flushes the published resources cache.<p>
1236     *
1237     * @deprecated use {@link #flushCache(CacheType[])} instead
1238     */
1239    @Deprecated
1240    public void flushPublishedResources() {
1241
1242        flushCache(CacheType.PUBLISHED_RESOURCES);
1243    }
1244
1245    /**
1246     * Flushes the publish history.<p>
1247     *
1248     * @deprecated use {@link #flushCache(CacheType[])} instead
1249     */
1250    @Deprecated
1251    public void flushPublishJobHistory() {
1252
1253        flushCache(CacheType.PUBLISH_HISTORY);
1254    }
1255
1256    /**
1257     * Flushes the publish queue.<p>
1258     *
1259     * @deprecated use {@link #flushCache(CacheType[])} instead
1260     */
1261    @Deprecated
1262    public void flushPublishJobs() {
1263
1264        flushCache(CacheType.PUBLISH_QUEUE);
1265    }
1266
1267    /**
1268     * Flushes the resource list cache.<p>
1269     *
1270     * @deprecated use {@link #flushCache(CacheType[])} instead
1271     */
1272    @Deprecated
1273    public void flushResourceLists() {
1274
1275        flushCache(CacheType.RESOURCE_LIST);
1276    }
1277
1278    /**
1279     * Flushes the resource cache.<p>
1280     *
1281     * @deprecated use {@link #flushCache(CacheType[])} instead
1282     */
1283    @Deprecated
1284    public void flushResources() {
1285
1286        flushCache(CacheType.RESOURCE);
1287    }
1288
1289    /**
1290     * Flushes the role lists cache.<p>
1291     *
1292     * @deprecated use {@link #flushCache(CacheType[])} instead
1293     */
1294    @Deprecated
1295    public void flushRoleLists() {
1296
1297        flushCache(CacheType.ROLE_LIST);
1298    }
1299
1300    /**
1301     * Flushes the roles cache.<p>
1302     *
1303     * @deprecated use {@link #flushCache(CacheType[])} instead
1304     */
1305    @Deprecated
1306    public void flushRoles() {
1307
1308        flushCache(CacheType.HAS_ROLE);
1309    }
1310
1311    /**
1312     * Flushes the user groups cache.<p>
1313     *
1314     * @deprecated use {@link #flushCache(CacheType[])} instead
1315     */
1316    @Deprecated
1317    public void flushUserGroups() {
1318
1319        flushCache(CacheType.USERGROUPS);
1320        flushCache(CacheType.USER_LIST);
1321    }
1322
1323    /**
1324     * Flushes the users cache.<p>
1325     *
1326     * @deprecated use {@link #flushCache(CacheType[])} instead
1327     */
1328    @Deprecated
1329    public void flushUsers() {
1330
1331        flushCache(CacheType.USER);
1332    }
1333
1334    /**
1335     * Flushes the vfs object cache.<p>
1336     *
1337     * @deprecated use {@link #flushCache(CacheType[])} instead
1338     */
1339    @Deprecated
1340    public void flushVfsObjects() {
1341
1342        flushCache(CacheType.VFS_OBJECT);
1343    }
1344
1345    /**
1346     * Flushes the xml permanent entities cache.<p>
1347     *
1348     * @deprecated use {@link #flushCache(CacheType[])} instead
1349     */
1350    @Deprecated
1351    public void flushXmlPermanentEntities() {
1352
1353        flushCache(CacheType.XML_ENTITY_PERM);
1354    }
1355
1356    /**
1357     * Flushes the xml temporary entities cache.<p>
1358     *
1359     * @deprecated use {@link #flushCache(CacheType[])} instead
1360     */
1361    @Deprecated
1362    public void flushXmlTemporaryEntities() {
1363
1364        flushCache(CacheType.XML_ENTITY_TEMP);
1365    }
1366
1367    /**
1368     * Returns all cached lock root paths.<p>
1369     *
1370     * @return a list of {@link String} objects
1371     */
1372    public List<String> getAllCachedLockPaths() {
1373
1374        return new ArrayList<String>(m_cacheLock.keySet());
1375    }
1376
1377    /**
1378     * Returns all cached locks.<p>
1379     *
1380     * @return a list of {@link CmsLock} objects
1381     */
1382    public List<CmsLock> getAllCachedLocks() {
1383
1384        return new ArrayList<CmsLock>(m_cacheLock.values());
1385    }
1386
1387    /**
1388     * Returns all cached publish jobs in the queue as ordered list.<p>
1389     *
1390     * @return all cached publish jobs
1391     */
1392    @SuppressWarnings("unchecked")
1393    public List<CmsPublishJobInfoBean> getAllCachedPublishJobs() {
1394
1395        return new ArrayList<CmsPublishJobInfoBean>(m_publishQueue);
1396    }
1397
1398    /**
1399     * Returns all cached publish jobs in the history as ordered list.<p>
1400     *
1401     * @return all cached publish jobs
1402     */
1403    @SuppressWarnings("unchecked")
1404    public List<CmsPublishJobInfoBean> getAllCachedPublishJobsInHistory() {
1405
1406        return new ArrayList<CmsPublishJobInfoBean>(m_publishHistory);
1407    }
1408
1409    /**
1410     * Returns the ACL cached with the given cache key or <code>null</code> if not found.<p>
1411     *
1412     * @param key the cache key to look for
1413     *
1414     * @return the ACL cached with the given cache key
1415     */
1416    public CmsAccessControlList getCachedACL(String key) {
1417
1418        return m_cacheAccessControlList.get(key);
1419    }
1420
1421    /**
1422     * Returns the xml content definition cached with the given cache key or <code>null</code> if not found.<p>
1423     *
1424     * @param key the cache key to look for
1425     *
1426     * @return the xml content definition cached with the given cache key
1427     */
1428    public CmsXmlContentDefinition getCachedContentDefinition(String key) {
1429
1430        return m_cacheContentDefinitions.get(key);
1431    }
1432
1433    /**
1434     * Returns the group cached with the given cache key or <code>null</code> if not found.<p>
1435     *
1436     * @param key the cache key to look for, this may be the group's uuid or the fqn
1437     *
1438     * @return the group cached with the given cache key
1439     */
1440    public CmsGroup getCachedGroup(String key) {
1441
1442        return m_cacheGroup.get(key);
1443    }
1444
1445    /**
1446     * Returns the locale cached with the given cache key or <code>null</code> if not found.<p>
1447     *
1448     * @param key the cache key to look for
1449     *
1450     * @return the locale cached with the given cache key
1451     */
1452    public Locale getCachedLocale(String key) {
1453
1454        if (m_cacheLocale == null) {
1455            // this may be accessed before initialization
1456            return null;
1457        }
1458        return m_cacheLocale.get(key);
1459    }
1460
1461    /**
1462     * Returns the lock cached with the given root path or <code>null</code> if not found.<p>
1463     *
1464     * @param rootPath the root path to look for
1465     *
1466     * @return the lock cached with the given root path
1467     */
1468    public CmsLock getCachedLock(String rootPath) {
1469
1470        return m_cacheLock.get(rootPath);
1471    }
1472
1473    /**
1474     * Returns the memory object cached with the given cache key or <code>null</code> if not found.<p>
1475     *
1476     * @param key the cache key to look for
1477     *
1478     * @return the memory object cached with the given cache key
1479     */
1480    public Object getCachedMemObject(String key) {
1481
1482        return m_cacheMemObject.get(key);
1483    }
1484
1485    /**
1486     * Returns the organizational unit cached with the given cache key or <code>null</code> if not found.<p>
1487     *
1488     * @param key the cache key to look for, this may be the organizational unit's uuid or the fqn
1489     *
1490     * @return the organizational unit cached with the given cache key
1491     */
1492    public CmsOrganizationalUnit getCachedOrgUnit(String key) {
1493
1494        return m_cacheOrgUnit.get(key);
1495    }
1496
1497    /**
1498     * Returns the permission check result cached with the given cache key or <code>null</code> if not found.<p>
1499     *
1500     * @param key the cache key to look for
1501     *
1502     * @return the permission check result cached with the given cache key
1503     */
1504    public I_CmsPermissionHandler.CmsPermissionCheckResult getCachedPermission(String key) {
1505
1506        return m_cachePermission.get(key);
1507    }
1508
1509    /**
1510     * Returns the project cached with the given cache key or <code>null</code> if not found.<p>
1511     *
1512     * @param key the cache key to look for, this may be the project's uuid or the fqn
1513     *
1514     * @return the project cached with the given cache key
1515     */
1516    public CmsProject getCachedProject(String key) {
1517
1518        return m_cacheProject.get(key);
1519    }
1520
1521    /**
1522     * Returns the project resources list cached with the given cache key or <code>null</code> if not found.<p>
1523     *
1524     * @param key the cache key to look for
1525     *
1526     * @return the project resources list cached with the given cache key
1527     */
1528    public List<CmsResource> getCachedProjectResources(String key) {
1529
1530        return m_cacheProjectResources.get(key);
1531    }
1532
1533    /**
1534     * Returns the property cached with the given cache key or <code>null</code> if not found.<p>
1535     *
1536     * @param key the cache key to look for
1537     *
1538     * @return the property cached with the given cache key
1539     */
1540    public CmsProperty getCachedProperty(String key) {
1541
1542        return m_cacheProperty.get(key);
1543    }
1544
1545    /**
1546     * Returns the property list cached with the given cache key or <code>null</code> if not found.<p>
1547     *
1548     * @param key the cache key to look for
1549     *
1550     * @return the property list cached with the given cache key
1551     */
1552    public List<CmsProperty> getCachedPropertyList(String key) {
1553
1554        return m_cachePropertyList.get(key);
1555    }
1556
1557    /**
1558     * Returns the published resources list cached with the given cache key or <code>null</code> if not found.<p>
1559     *
1560     * @param cacheKey the cache key to look for
1561     *
1562     * @return the published resources list cached with the given cache key
1563     */
1564    public List<CmsPublishedResource> getCachedPublishedResources(String cacheKey) {
1565
1566        return m_cachePublishedResources.get(cacheKey);
1567    }
1568
1569    /**
1570     * Returns the publish job with the given cache key or <code>null</code> if not found.<p>
1571     *
1572     * @param key  the cache key to look for
1573     *
1574     * @return the publish job with the given cache key
1575     */
1576    public CmsPublishJobInfoBean getCachedPublishJob(String key) {
1577
1578        synchronized (m_publishQueue) {
1579            for (Object obj : m_publishQueue) {
1580                CmsPublishJobInfoBean publishJob = (CmsPublishJobInfoBean)obj;
1581                if (publishJob.getPublishHistoryId().toString().equals(key)) {
1582                    return publishJob;
1583                }
1584            }
1585        }
1586        return null;
1587    }
1588
1589    /**
1590     * Returns the publish job from the history with the given cache key or <code>null</code> if not found.<p>
1591     *
1592     * @param key  the cache key to look for
1593     *
1594     * @return the publish job with the given cache key
1595     */
1596    public CmsPublishJobInfoBean getCachedPublishJobInHistory(String key) {
1597
1598        for (Object obj : m_publishHistory) {
1599            CmsPublishJobInfoBean publishJob = (CmsPublishJobInfoBean)obj;
1600            if (publishJob.getPublishHistoryId().toString().equals(key)) {
1601                return publishJob;
1602            }
1603        }
1604
1605        return null;
1606    }
1607
1608    /**
1609     * Returns the resource cached with the given cache key or <code>null</code> if not found.<p>
1610     *
1611     * @param key the cache key to look for
1612     *
1613     * @return the resource cached with the given cache key
1614     */
1615    public CmsResource getCachedResource(String key) {
1616
1617        return m_cacheResource.get(key);
1618    }
1619
1620    /**
1621     * Returns the resource list cached with the given cache key or <code>null</code> if not found.<p>
1622     *
1623     * @param key the cache key to look for
1624     *
1625     * @return the resource list cached with the given cache key
1626     */
1627    public List<CmsResource> getCachedResourceList(String key) {
1628
1629        return m_cacheResourceList.get(key);
1630    }
1631
1632    /**
1633     * Returns the value cached with the given cache key or <code>null</code> if not found.<p>
1634     *
1635     * @param key the cache key to look for
1636     *
1637     * @return if the user has the given role
1638     */
1639    public Boolean getCachedRole(String key) {
1640
1641        return m_cacheHasRoles.get(key);
1642    }
1643
1644    /**
1645     * Returns the value cached with the given cache key or <code>null</code> if not found.<p>
1646     *
1647     * @param key the cache key to look for
1648     *
1649     * @return list of roles
1650     */
1651    public List<CmsRole> getCachedRoleList(String key) {
1652
1653        return m_cacheRoleLists.get(key);
1654    }
1655
1656    /**
1657     * Returns the user cached with the given cache key or <code>null</code> if not found.<p>
1658     *
1659     * @param key the cache key to look for, this may be the user's uuid or the fqn
1660     *
1661     * @return the user cached with the given cache key
1662     */
1663    public CmsUser getCachedUser(String key) {
1664
1665        return m_cacheUser.get(key);
1666    }
1667
1668    /**
1669     * Returns the user groups list cached with the given cache key or <code>null</code> if not found.<p>
1670     *
1671     * @param key the cache key to look for
1672     *
1673     * @return the user groups list cached with the given cache key
1674     */
1675    public List<CmsGroup> getCachedUserGroups(String key) {
1676
1677        return m_cacheUserGroups.get(key);
1678    }
1679
1680    /**
1681     * Returns the user list cached with the given cache key or <code>null</code> if not found.<p>
1682     *
1683     * @param key the cache key to look for
1684     *
1685     * @return the user groups list cached with the given cache key
1686     */
1687    public List<CmsUser> getCachedUserList(String key) {
1688
1689        return m_cacheUserList.get(key);
1690    }
1691
1692    /**
1693     * Returns the vfs object cached with the given cache key or <code>null</code> if not found.<p>
1694     *
1695     * @param key the cache key to look for
1696     *
1697     * @return the vfs object cached with the given cache key
1698     */
1699    public Object getCachedVfsObject(String key) {
1700
1701        return m_cacheVfsObject.get(key);
1702    }
1703
1704    /**
1705     * Returns the xml permanent entity content cached with the given systemId or <code>null</code> if not found.<p>
1706     *
1707     * @param systemId the cache key to look for
1708     *
1709     * @return the xml permanent entity content cached with the given cache key
1710     */
1711    public byte[] getCachedXmlPermanentEntity(String systemId) {
1712
1713        return m_cacheXmlPermanentEntity.get(systemId);
1714    }
1715
1716    /**
1717     * Returns the xml temporary entity content cached with the given cache key or <code>null</code> if not found.<p>
1718     *
1719     * @param key the cache key to look for
1720     *
1721     * @return the xml temporary entity content cached with the given cache key
1722     */
1723    public byte[] getCachedXmlTemporaryEntity(String key) {
1724
1725        return m_cacheXmlTemporaryEntity.get(key);
1726    }
1727
1728    /**
1729     * Returns the configuration.<p>
1730     *
1731     * @return the configuration
1732     */
1733    public CmsMemoryMonitorConfiguration getConfiguration() {
1734
1735        return m_configuration;
1736    }
1737
1738    /**
1739     * Returns the next publish job from the publish job queue.<p>
1740     *
1741     * @return the next publish job
1742     */
1743    public CmsPublishJobInfoBean getFirstCachedPublishJob() {
1744
1745        synchronized (m_publishQueue) {
1746            if (!m_publishQueue.isEmpty()) {
1747                return (CmsPublishJobInfoBean)m_publishQueue.get();
1748            } else {
1749                return null;
1750            }
1751        }
1752    }
1753
1754    /**
1755     * Returns the log count.<p>
1756     *
1757     * @return the log count
1758     */
1759    public int getLogCount() {
1760
1761        return m_logCount;
1762    }
1763
1764    /**
1765     * Returns the current memory status.<p>
1766     *
1767     * @return the memory status
1768     */
1769    public CmsMemoryStatus getMemoryStatus() {
1770
1771        m_memoryCurrent.update();
1772        return m_memoryCurrent;
1773    }
1774
1775    /**
1776     * Initializes the monitor with the provided configuration.<p>
1777     *
1778     * @param configuration the configuration to use
1779     */
1780    public void initialize(CmsSystemConfiguration configuration) {
1781
1782        CmsCacheSettings cacheSettings = configuration.getCacheSettings();
1783
1784        m_memoryAverage = new CmsMemoryStatus();
1785        m_memoryCurrent = new CmsMemoryStatus();
1786
1787        m_warningSendSinceLastStatus = false;
1788        m_warningLoggedSinceLastStatus = false;
1789        m_lastEmailWarning = 0;
1790        m_lastEmailStatus = 0;
1791        m_lastLogStatus = 0;
1792        m_lastLogWarning = 0;
1793        m_lastClearCache = 0;
1794        m_configuration = configuration.getCmsMemoryMonitorConfiguration();
1795
1796        m_intervalWarning = 720 * 60000;
1797        m_maxUsagePercent = 90;
1798
1799        m_intervalEmail = m_configuration.getEmailInterval() * 1000;
1800        m_intervalLog = m_configuration.getLogInterval() * 1000;
1801
1802        if (m_configuration.getWarningInterval() > 0) {
1803            m_intervalWarning = m_configuration.getWarningInterval();
1804        }
1805        m_intervalWarning *= 1000;
1806
1807        if (m_configuration.getMaxUsagePercent() > 0) {
1808            m_maxUsagePercent = m_configuration.getMaxUsagePercent();
1809        }
1810
1811        if (CmsLog.INIT.isInfoEnabled()) {
1812            CmsLog.INIT.info(
1813                Messages.get().getBundle().key(Messages.LOG_MM_INTERVAL_LOG_1, new Integer(m_intervalLog / 1000)));
1814            CmsLog.INIT.info(
1815                Messages.get().getBundle().key(Messages.LOG_MM_INTERVAL_EMAIL_1, new Integer(m_intervalEmail / 1000)));
1816            CmsLog.INIT.info(
1817                Messages.get().getBundle().key(
1818                    Messages.LOG_MM_INTERVAL_WARNING_1,
1819                    new Integer(m_intervalWarning / 1000)));
1820            CmsLog.INIT.info(
1821                Messages.get().getBundle().key(Messages.LOG_MM_INTERVAL_MAX_USAGE_1, new Integer(m_maxUsagePercent)));
1822
1823            if ((m_configuration.getEmailReceiver() == null) || (m_configuration.getEmailSender() == null)) {
1824                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.LOG_MM_EMAIL_DISABLED_0));
1825            } else {
1826                CmsLog.INIT.info(
1827                    Messages.get().getBundle().key(Messages.LOG_MM_EMAIL_SENDER_1, m_configuration.getEmailSender()));
1828                Iterator<String> i = m_configuration.getEmailReceiver().iterator();
1829                int n = 0;
1830                while (i.hasNext()) {
1831                    CmsLog.INIT.info(
1832                        Messages.get().getBundle().key(Messages.LOG_MM_EMAIL_RECEIVER_2, new Integer(n + 1), i.next()));
1833                    n++;
1834                }
1835            }
1836        }
1837
1838        // create and register all system caches
1839
1840        // temporary xml entities cache
1841        m_cacheXmlTemporaryEntity = createLRUCacheMap(128);
1842        register(CmsXmlEntityResolver.class.getName() + ".xmlEntityTemporaryCache", m_cacheXmlTemporaryEntity);
1843
1844        // permanent xml entities cache
1845        m_cacheXmlPermanentEntity = new ConcurrentHashMap<String, byte[]>(32);
1846        register(CmsXmlEntityResolver.class.getName() + ".xmlEntityPermanentCache", m_cacheXmlPermanentEntity);
1847
1848        // xml content definitions cache
1849        m_cacheContentDefinitions = createLRUCacheMap(64);
1850        register(CmsXmlEntityResolver.class.getName() + ".contentDefinitionsCache", m_cacheContentDefinitions);
1851
1852        // lock cache
1853        m_cacheLock = new ConcurrentHashMap<String, CmsLock>();
1854        register(CmsLockManager.class.getName(), m_cacheLock);
1855
1856        // locale cache
1857        m_cacheLocale = new ConcurrentHashMap<String, Locale>();
1858        register(CmsLocaleManager.class.getName(), m_cacheLocale);
1859
1860        // permissions cache
1861        m_cachePermission = createLRUCacheMap(cacheSettings.getPermissionCacheSize());
1862        register(CmsSecurityManager.class.getName(), m_cachePermission);
1863
1864        // user cache
1865        m_cacheUser = createLRUCacheMap(cacheSettings.getUserCacheSize());
1866        register(CmsDriverManager.class.getName() + ".userCache", m_cacheUser);
1867
1868        // user list cache
1869        m_cacheUserList = createLRUCacheMap(cacheSettings.getUserCacheSize());
1870        register(CmsDriverManager.class.getName() + ".userListCache", m_cacheUserList);
1871
1872        // group cache
1873        m_cacheGroup = createLRUCacheMap(cacheSettings.getGroupCacheSize());
1874        register(CmsDriverManager.class.getName() + ".groupCache", m_cacheGroup);
1875
1876        // organizational unit cache
1877        m_cacheOrgUnit = createLRUCacheMap(cacheSettings.getOrgUnitCacheSize());
1878        register(CmsDriverManager.class.getName() + ".orgUnitCache", m_cacheOrgUnit);
1879
1880        // user groups list cache
1881        m_cacheUserGroups = createLRUCacheMap(cacheSettings.getUserGroupsCacheSize());
1882        register(CmsDriverManager.class.getName() + ".userGroupsCache", m_cacheUserGroups);
1883
1884        // project cache
1885        m_cacheProject = createLRUCacheMap(cacheSettings.getProjectCacheSize());
1886        register(CmsDriverManager.class.getName() + ".projectCache", m_cacheProject);
1887
1888        // project resources cache cache
1889        m_cacheProjectResources = createLRUCacheMap(cacheSettings.getProjectResourcesCacheSize());
1890        register(CmsDriverManager.class.getName() + ".projectResourcesCache", m_cacheProjectResources);
1891
1892        // publish history
1893        int size = configuration.getPublishManager().getPublishHistorySize();
1894        Buffer buffer = CmsPublishHistory.getQueue(size);
1895        m_publishHistory = SynchronizedBuffer.decorate(buffer);
1896        register(CmsPublishHistory.class.getName() + ".publishHistory", buffer);
1897
1898        // publish queue
1899        buffer = CmsPublishQueue.getQueue();
1900        m_publishQueue = SynchronizedBuffer.decorate(buffer);
1901        register(CmsPublishQueue.class.getName() + ".publishQueue", buffer);
1902
1903        // resource cache
1904        m_cacheResource = createLRUCacheMap(cacheSettings.getResourceCacheSize());
1905        register(CmsDriverManager.class.getName() + ".resourceCache", m_cacheResource);
1906
1907        // roles cache
1908        m_cacheHasRoles = createLRUCacheMap(cacheSettings.getRolesCacheSize());
1909        register(CmsDriverManager.class.getName() + ".rolesCache", m_cacheHasRoles);
1910
1911        // role lists cache
1912        m_cacheRoleLists = createLRUCacheMap(cacheSettings.getRolesCacheSize());
1913        register(CmsDriverManager.class.getName() + ".roleListsCache", m_cacheRoleLists);
1914
1915        // resource list cache
1916        m_cacheResourceList = createLRUCacheMap(cacheSettings.getResourcelistCacheSize());
1917        register(CmsDriverManager.class.getName() + ".resourceListCache", m_cacheResourceList);
1918
1919        // property cache
1920        m_cacheProperty = createLRUCacheMap(cacheSettings.getPropertyCacheSize());
1921        register(CmsDriverManager.class.getName() + ".propertyCache", m_cacheProperty);
1922
1923        // property list cache
1924        m_cachePropertyList = createLRUCacheMap(cacheSettings.getPropertyListsCacheSize());
1925        register(CmsDriverManager.class.getName() + ".propertyListCache", m_cachePropertyList);
1926
1927        // published resources list cache
1928        m_cachePublishedResources = createLRUCacheMap(5);
1929        register(CmsDriverManager.class.getName() + ".publishedResourcesCache", m_cachePublishedResources);
1930
1931        // acl cache
1932        m_cacheAccessControlList = createLRUCacheMap(cacheSettings.getAclCacheSize());
1933        register(CmsDriverManager.class.getName() + ".accessControlListCache", m_cacheAccessControlList);
1934
1935        // vfs object cache
1936        m_cacheVfsObject = new ConcurrentHashMap<String, Object>();
1937        register(CmsVfsMemoryObjectCache.class.getName(), m_cacheVfsObject);
1938
1939        // memory object cache
1940        m_cacheMemObject = new ConcurrentHashMap<String, Object>();
1941        register(CmsMemoryObjectCache.class.getName(), m_cacheMemObject);
1942
1943        if (LOG.isDebugEnabled()) {
1944            // this will happen only once during system startup
1945            LOG.debug(Messages.get().getBundle().key(Messages.LOG_MM_CREATED_1, new Date(System.currentTimeMillis())));
1946        }
1947    }
1948
1949    /**
1950     * Checks if the property cache is enabled.<p>
1951     *
1952     * @return <code>true</code> if the property cache is enabled
1953     *
1954     * @deprecated use {@link #isEnabled(CacheType)} instead
1955     */
1956    @Deprecated
1957    public boolean isCacheProperty() {
1958
1959        return isEnabled(CacheType.PROPERTY);
1960    }
1961
1962    /**
1963     * Checks if the property list cache is enabled.<p>
1964     *
1965     * @return <code>true</code> if the property list cache is enabled
1966     *
1967     * @deprecated use {@link #isEnabled(CacheType)} instead
1968     */
1969    @Deprecated
1970    public boolean isCachePropertyList() {
1971
1972        return isEnabled(CacheType.PROPERTY_LIST);
1973    }
1974
1975    /**
1976     * Checks if the resource cache is enabled.<p>
1977     *
1978     * @return <code>true</code> if the resource cache is enabled
1979     *
1980     * @deprecated use {@link #isEnabled(CacheType)} instead
1981     */
1982    @Deprecated
1983    public boolean isCacheResource() {
1984
1985        return isEnabled(CacheType.RESOURCE);
1986    }
1987
1988    /**
1989     * Checks if the resource list cache is enabled.<p>
1990     *
1991     * @return <code>true</code> if the resource list cache is enabled
1992     *
1993     * @deprecated use {@link #isEnabled(CacheType)} instead
1994     */
1995    @Deprecated
1996    public boolean isCacheResourceList() {
1997
1998        return isEnabled(CacheType.RESOURCE_LIST);
1999    }
2000
2001    /**
2002     * Checks if the given cache is enabled.<p>
2003     *
2004     * @param type the cache type to check
2005     *
2006     * @return <code>true</code> if the given cache is enabled
2007     */
2008    public boolean isEnabled(CacheType type) {
2009
2010        return (m_disabled.get(type) == null);
2011    }
2012
2013    /**
2014     * Checks if there is a registered monitored object with the given key.<p>
2015     *
2016     * @param key the key to look for
2017     *
2018     * @return <code>true</code> if there is a registered monitored object with the given key
2019     */
2020    public boolean isMonitoring(String key) {
2021
2022        return (m_monitoredObjects.get(key) != null);
2023    }
2024
2025    /**
2026     * @see org.opencms.scheduler.I_CmsScheduledJob#launch(CmsObject, Map)
2027     */
2028    public String launch(CmsObject cms, Map<String, String> parameters) throws Exception {
2029
2030        CmsMemoryMonitor monitor = OpenCms.getMemoryMonitor();
2031
2032        // make sure job is not launched twice
2033        if (m_currentlyRunning) {
2034            return null;
2035        }
2036
2037        try {
2038            m_currentlyRunning = true;
2039
2040            // update the memory status
2041            monitor.updateStatus();
2042
2043            // check if the system is in a low memory condition
2044            if (monitor.lowMemory()) {
2045                // log warning
2046                monitor.monitorWriteLog(true);
2047                // send warning email
2048                monitor.monitorSendEmail(true);
2049                // clean up caches
2050                monitor.clearCaches();
2051            }
2052
2053            // check if regular a log entry must be written
2054            if ((System.currentTimeMillis() - monitor.m_lastLogStatus) > monitor.m_intervalLog) {
2055                monitor.monitorWriteLog(false);
2056            }
2057
2058            // check if the memory status email must be send
2059            if ((System.currentTimeMillis() - monitor.m_lastEmailStatus) > monitor.m_intervalEmail) {
2060                monitor.monitorSendEmail(false);
2061            }
2062        } finally {
2063            // make sure state is reset even if an error occurs,
2064            // otherwise MM will not be executed after an error
2065            m_currentlyRunning = false;
2066        }
2067
2068        return null;
2069    }
2070
2071    /**
2072     * Returns true if the system runs low on memory.<p>
2073     *
2074     * @return true if the system runs low on memory
2075     */
2076    public boolean lowMemory() {
2077
2078        return ((m_maxUsagePercent > 0) && (m_memoryCurrent.getUsage() > m_maxUsagePercent));
2079    }
2080
2081    /**
2082     * Adds a new object to the monitor.<p>
2083     *
2084     * @param objectName name of the object
2085     * @param object the object for monitoring
2086     */
2087    public void register(String objectName, Object object) {
2088
2089        if (enabled()) {
2090            m_monitoredObjects.put(objectName, object);
2091        }
2092    }
2093
2094    /**
2095     * Checks if some kind of persistence is required.<p>
2096     *
2097     * This could be overwritten in a distributed environment.<p>
2098     *
2099     * @return <code>true</code> if some kind of persistence is required
2100     */
2101    public boolean requiresPersistency() {
2102
2103        return true;
2104    }
2105
2106    /**
2107     * Sets if the property cache is enabled.<p>
2108     *
2109     * @param cacheProperty if the property cache is enabled
2110     *
2111     * @deprecated use {@link #enableCache(CacheType[])} or {@link #disableCache(CacheType[])} instead
2112     */
2113    @Deprecated
2114    public void setCacheProperty(boolean cacheProperty) {
2115
2116        if (cacheProperty) {
2117            enableCache(CacheType.PROPERTY);
2118        } else {
2119            disableCache(CacheType.PROPERTY);
2120        }
2121    }
2122
2123    /**
2124     * Sets if the property list cache is enabled.<p>
2125     *
2126     * @param cachePropertyList if the property list cache is enabled
2127     *
2128     * @deprecated use {@link #enableCache(CacheType[])} or {@link #disableCache(CacheType[])} instead
2129     */
2130    @Deprecated
2131    public void setCachePropertyList(boolean cachePropertyList) {
2132
2133        if (cachePropertyList) {
2134            enableCache(CacheType.PROPERTY_LIST);
2135        } else {
2136            disableCache(CacheType.PROPERTY_LIST);
2137        }
2138    }
2139
2140    /**
2141     * Sets if the resource cache is enabled.<p>
2142     *
2143     * @param cacheResource if the resource cache is enabled
2144     *
2145     * @deprecated use {@link #enableCache(CacheType[])} or {@link #disableCache(CacheType[])} instead
2146     */
2147    @Deprecated
2148    public void setCacheResource(boolean cacheResource) {
2149
2150        if (cacheResource) {
2151            enableCache(CacheType.RESOURCE);
2152        } else {
2153            disableCache(CacheType.RESOURCE);
2154        }
2155    }
2156
2157    /**
2158     * Sets if the resource list cache is enabled.<p>
2159     *
2160     * @param cacheResourceList if the resource list cache is enabled
2161     *
2162     * @deprecated use {@link #enableCache(CacheType[])} or {@link #disableCache(CacheType[])} instead
2163     */
2164    @Deprecated
2165    public void setCacheResourceList(boolean cacheResourceList) {
2166
2167        if (cacheResourceList) {
2168            enableCache(CacheType.RESOURCE_LIST);
2169        } else {
2170            disableCache(CacheType.RESOURCE_LIST);
2171        }
2172    }
2173
2174    /**
2175     * Flushes all cached objects.<p>
2176     *
2177     * @throws Exception if something goes wrong
2178     */
2179    public void shutdown() throws Exception {
2180
2181        for (CacheType type : CacheType.values()) {
2182            flushCache(type);
2183        }
2184    }
2185
2186    /**
2187     * Removes the given xml content definition from the cache.<p>
2188     *
2189     * @param key the cache key to remove from cache
2190     */
2191    public void uncacheContentDefinition(String key) {
2192
2193        m_cacheContentDefinitions.remove(key);
2194    }
2195
2196    /**
2197     * Removes the given group from the cache.<p>
2198     *
2199     * The group is removed by name AND also by uuid.<p>
2200     *
2201     * @param group the group to remove from cache
2202     */
2203    public void uncacheGroup(CmsGroup group) {
2204
2205        m_cacheGroup.remove(group.getId().toString());
2206        m_cacheGroup.remove(group.getName());
2207    }
2208
2209    /**
2210     * Removes the cached lock for the given root path from the cache.<p>
2211     *
2212     * @param rootPath the root path of the lock to remove from cache
2213     */
2214    public void uncacheLock(String rootPath) {
2215
2216        m_cacheLock.remove(rootPath);
2217    }
2218
2219    /**
2220     * Removes the given organizational unit from the cache.<p>
2221     *
2222     * The organizational unit is removed by name AND also by uuid.<p>
2223     *
2224     * @param orgUnit the organizational unit to remove from cache
2225     */
2226    public void uncacheOrgUnit(CmsOrganizationalUnit orgUnit) {
2227
2228        m_cacheOrgUnit.remove(orgUnit.getId().toString());
2229        m_cacheOrgUnit.remove(orgUnit.getName());
2230    }
2231
2232    /**
2233     * Removes the given project from the cache.<p>
2234     *
2235     * The project is removed by name AND also by uuid.<p>
2236     *
2237     * @param project the project to remove from cache
2238     */
2239    public void uncacheProject(CmsProject project) {
2240
2241        m_cacheProject.remove(project.getUuid().toString());
2242        m_cacheProject.remove(project.getName());
2243    }
2244
2245    /**
2246     * Removes the given publish job from the cache.<p>
2247     *
2248     * @param publishJob the publish job to remove
2249     */
2250    public void uncachePublishJob(CmsPublishJobInfoBean publishJob) {
2251
2252        m_publishQueue.remove(publishJob);
2253    }
2254
2255    /**
2256     * Removes the given publish job from the history.<p>
2257     *
2258     * @param publishJob the publish job to remove
2259     */
2260    public void uncachePublishJobInHistory(CmsPublishJobInfoBean publishJob) {
2261
2262        m_publishHistory.remove(publishJob);
2263    }
2264
2265    /**
2266     * Removes the given user from the cache.<p>
2267     *
2268     * The user is removed by name AND also by uuid.<p>
2269     *
2270     * @param user the user to remove from cache
2271     */
2272    public void uncacheUser(CmsUser user) {
2273
2274        m_cacheUser.remove(user.getId().toString());
2275        m_cacheUser.remove(user.getName());
2276    }
2277
2278    /**
2279     * Removes the given vfs object from the cache.<p>
2280     *
2281     * @param key the cache key to remove from cache
2282     */
2283    public void uncacheVfsObject(String key) {
2284
2285        m_cacheVfsObject.remove(key);
2286    }
2287
2288    /**
2289     * Removes the given xml temporary entity from the cache.<p>
2290     *
2291     * @param key the cache key to remove from cache
2292     */
2293    public void uncacheXmlTemporaryEntity(String key) {
2294
2295        m_cacheXmlTemporaryEntity.remove(key);
2296    }
2297
2298    /**
2299     * Clears the OpenCms caches.<p>
2300     */
2301    protected void clearCaches() {
2302
2303        if ((m_lastClearCache + INTERVAL_CLEAR) > System.currentTimeMillis()) {
2304            // if the cache has already been cleared less then 15 minutes ago we skip this because
2305            // clearing the caches to often will hurt system performance and the
2306            // setup seems to be in trouble anyway
2307            return;
2308        }
2309        m_lastClearCache = System.currentTimeMillis();
2310        if (LOG.isWarnEnabled()) {
2311            LOG.warn(Messages.get().getBundle().key(Messages.LOG_CLEAR_CACHE_MEM_CONS_0));
2312        }
2313        OpenCms.fireCmsEvent(
2314            new CmsEvent(I_CmsEventListener.EVENT_CLEAR_CACHES, Collections.<String, Object> emptyMap()));
2315        System.gc();
2316    }
2317
2318    /**
2319     * Returns the cache costs of a monitored object.<p>
2320     *
2321     * <code>obj</code> must be of type {@link CmsLruCache}.<p>
2322     *
2323     * @param obj the object
2324     *
2325     * @return the cache costs or "-"
2326     */
2327    protected long getCosts(Object obj) {
2328
2329        long costs = 0;
2330        if (obj instanceof CmsLruCache) {
2331            costs = ((CmsLruCache)obj).getObjectCosts();
2332            if (costs < 0) {
2333                costs = 0;
2334            }
2335        }
2336
2337        return costs;
2338    }
2339
2340    /**
2341     * Returns the number of items within a monitored object.<p>
2342     *
2343     * <code>obj</code> must be of type {@link CmsLruCache} or {@link Map}.<p>
2344     *
2345     * @param obj the object
2346     *
2347     * @return the number of items or "-"
2348     */
2349    protected String getItems(Object obj) {
2350
2351        if (obj instanceof CmsLruCache) {
2352            return Integer.toString(((CmsLruCache)obj).size());
2353        }
2354        if (obj instanceof Map) {
2355            return Integer.toString(((Map<?, ?>)obj).size());
2356        }
2357        return "-";
2358    }
2359
2360    /**
2361     * Returns the total size of key strings within a monitored map.<p>
2362     *
2363     * The keys must be of type {@link String}.<p>
2364     *
2365     * @param map the map
2366     * @param depth the max recursion depth for calculation the size
2367     *
2368     * @return total size of key strings
2369     */
2370    protected long getKeySize(Map<?, ?> map, int depth) {
2371
2372        long keySize = 0;
2373        try {
2374            Object[] values = map.values().toArray();
2375            for (int i = 0, s = values.length; i < s; i++) {
2376
2377                Object obj = values[i];
2378
2379                if ((obj instanceof Map) && (depth < MAX_DEPTH)) {
2380                    keySize += getKeySize((Map<?, ?>)obj, depth + 1);
2381                    continue;
2382                }
2383            }
2384            values = null;
2385
2386            Object[] keys = map.keySet().toArray();
2387            for (int i = 0, s = keys.length; i < s; i++) {
2388
2389                Object obj = keys[i];
2390
2391                if (obj instanceof String) {
2392                    String st = (String)obj;
2393                    keySize += (st.length() * 2);
2394                }
2395            }
2396        } catch (ConcurrentModificationException e) {
2397            // this might happen since even the .toArray() method internally creates an iterator
2398        } catch (Throwable t) {
2399            // catch all other exceptions otherwise the whole monitor will stop working
2400            if (LOG.isDebugEnabled()) {
2401                LOG.debug(Messages.get().getBundle().key(Messages.LOG_CAUGHT_THROWABLE_1, t.getMessage()));
2402            }
2403        }
2404
2405        return keySize;
2406    }
2407
2408    /**
2409     * Returns the total size of key strings within a monitored object.<p>
2410     *
2411     * <code>obj</code> must be of type {@link Map}, the keys must be of type {@link String}.<p>
2412     *
2413     * @param obj the object
2414     *
2415     * @return the total size of key strings
2416     */
2417    protected long getKeySize(Object obj) {
2418
2419        if (obj instanceof Map) {
2420            return getKeySize((Map<?, ?>)obj, 1);
2421        }
2422
2423        return 0;
2424    }
2425
2426    /**
2427     * Returns the max costs for all items within a monitored object.<p>
2428     *
2429     * <code>obj</code> must be of type {@link CmsLruCache} or {@link LRUMap}.<p>
2430     *
2431     * @param obj the object
2432     *
2433     * @return max cost limit or "-"
2434     */
2435    protected String getLimit(Object obj) {
2436
2437        if (obj instanceof CmsLruCache) {
2438            return Long.toString(((CmsLruCache)obj).getMaxCacheCosts());
2439        }
2440        if (obj instanceof LRUMap) {
2441            return Integer.toString(((LRUMap)obj).maxSize());
2442        }
2443
2444        return "-";
2445    }
2446
2447    /**
2448     * Sends a warning or status email with OpenCms Memory information.<p>
2449     *
2450     * @param warning if true, send a memory warning email
2451     */
2452    protected void monitorSendEmail(boolean warning) {
2453
2454        if ((m_configuration.getEmailSender() == null) || (m_configuration.getEmailReceiver() == null)) {
2455            // send no mails if not fully configured
2456            return;
2457        } else if (warning
2458            && (m_warningSendSinceLastStatus
2459                && !((m_intervalEmail <= 0)
2460                    && (System.currentTimeMillis() < (m_lastEmailWarning + m_intervalWarning))))) {
2461            // send no warning email if no status email has been send since the last warning
2462            // if status is disabled, send no warn email if warn interval has not passed
2463            return;
2464        } else if ((!warning) && (m_intervalEmail <= 0)) {
2465            // if email iterval is <= 0 status email is disabled
2466            return;
2467        }
2468        String date = CmsDateUtil.getDateTimeShort(System.currentTimeMillis());
2469        String subject;
2470        String content = "";
2471        if (warning) {
2472            m_warningSendSinceLastStatus = true;
2473            m_lastEmailWarning = System.currentTimeMillis();
2474            subject = "OpenCms Memory W A R N I N G ["
2475                + OpenCms.getSystemInfo().getServerName().toUpperCase()
2476                + "/"
2477                + date
2478                + "]";
2479            content += "W A R N I N G !\nOpenCms memory consumption on server "
2480                + OpenCms.getSystemInfo().getServerName().toUpperCase()
2481                + " has reached a critical level !\n\n"
2482                + "The configured limit is "
2483                + m_maxUsagePercent
2484                + "%\n\n";
2485        } else {
2486            m_warningSendSinceLastStatus = false;
2487            m_lastEmailStatus = System.currentTimeMillis();
2488            subject = "OpenCms Memory Status ["
2489                + OpenCms.getSystemInfo().getServerName().toUpperCase()
2490                + "/"
2491                + date
2492                + "]";
2493        }
2494
2495        content += "Memory usage report of OpenCms server "
2496            + OpenCms.getSystemInfo().getServerName().toUpperCase()
2497            + " at "
2498            + date
2499            + "\n\n"
2500            + "Memory maximum heap size: "
2501            + m_memoryCurrent.getMaxMemory()
2502            + " mb\n"
2503            + "Memory current heap size: "
2504            + m_memoryCurrent.getTotalMemory()
2505            + " mb\n\n"
2506            + "Memory currently used   : "
2507            + m_memoryCurrent.getUsedMemory()
2508            + " mb ("
2509            + m_memoryCurrent.getUsage()
2510            + "%)\n"
2511            + "Memory currently unused : "
2512            + m_memoryCurrent.getFreeMemory()
2513            + " mb\n\n\n";
2514
2515        if (warning) {
2516            content += "*** Please take action NOW to ensure that no OutOfMemoryException occurs.\n\n\n";
2517        }
2518
2519        CmsSessionManager sm = OpenCms.getSessionManager();
2520
2521        if (sm != null) {
2522            content += "Current status of the sessions:\n\n";
2523            content += "Logged in users          : " + sm.getSessionCountAuthenticated() + "\n";
2524            content += "Currently active sessions: " + sm.getSessionCountCurrent() + "\n";
2525            content += "Total created sessions   : " + sm.getSessionCountTotal() + "\n\n\n";
2526        }
2527
2528        sm = null;
2529
2530        content += "Current status of the caches:\n\n";
2531        List<String> keyList = new ArrayList<String>(m_monitoredObjects.keySet());
2532        Collections.sort(keyList);
2533        long totalSize = 0;
2534        for (Iterator<String> keys = keyList.iterator(); keys.hasNext();) {
2535            String key = keys.next();
2536            String[] shortKeys = key.split("\\.");
2537            String shortKey = shortKeys[shortKeys.length - 2] + '.' + shortKeys[shortKeys.length - 1];
2538            PrintfFormat form = new PrintfFormat("%9s");
2539            Object obj = m_monitoredObjects.get(key);
2540
2541            long size = getKeySize(obj) + getValueSize(obj) + getCosts(obj);
2542            totalSize += size;
2543
2544            content += new PrintfFormat("%-42.42s").sprintf(shortKey)
2545                + "  "
2546                + "Entries: "
2547                + form.sprintf(getItems(obj))
2548                + "   "
2549                + "Limit: "
2550                + form.sprintf(getLimit(obj))
2551                + "   "
2552                + "Size: "
2553                + form.sprintf(Long.toString(size))
2554                + "\n";
2555        }
2556        content += "\nTotal size of cache memory monitored: " + totalSize + " (" + (totalSize / 1048576) + ")\n\n";
2557
2558        String from = m_configuration.getEmailSender();
2559        List<InternetAddress> receivers = new ArrayList<InternetAddress>();
2560        List<String> receiverEmails = m_configuration.getEmailReceiver();
2561        try {
2562            if ((from != null) && (receiverEmails != null) && !receiverEmails.isEmpty()) {
2563                Iterator<String> i = receiverEmails.iterator();
2564                while (i.hasNext()) {
2565                    receivers.add(new InternetAddress(i.next()));
2566                }
2567                CmsSimpleMail email = new CmsSimpleMail();
2568                email.setFrom(from);
2569                email.setTo(receivers);
2570                email.setSubject(subject);
2571                email.setMsg(content);
2572                new CmsMailTransport(email).send();
2573            }
2574            if (LOG.isInfoEnabled()) {
2575                if (warning) {
2576                    LOG.info(Messages.get().getBundle().key(Messages.LOG_MM_WARNING_EMAIL_SENT_0));
2577                } else {
2578                    LOG.info(Messages.get().getBundle().key(Messages.LOG_MM_STATUS_EMAIL_SENT_0));
2579                }
2580            }
2581        } catch (Exception e) {
2582            e.printStackTrace();
2583        }
2584    }
2585
2586    /**
2587     * Write a warning or status log entry with OpenCms Memory information.<p>
2588     *
2589     * @param warning if true, write a memory warning log entry
2590     */
2591    protected void monitorWriteLog(boolean warning) {
2592
2593        if (!LOG.isWarnEnabled()) {
2594            // we need at last warn level for this output
2595            return;
2596        } else if ((!warning) && (!LOG.isInfoEnabled())) {
2597            // if not warning we need info level
2598            return;
2599        } else if (warning
2600            && (m_warningLoggedSinceLastStatus
2601                && !(((m_intervalLog <= 0)
2602                    && (System.currentTimeMillis() < (m_lastLogWarning + m_intervalWarning)))))) {
2603            // write no warning log if no status log has been written since the last warning
2604            // if status is disabled, log no warn entry if warn interval has not passed
2605            return;
2606        } else if ((!warning) && (m_intervalLog <= 0)) {
2607            // if log interval is <= 0 status log is disabled
2608            return;
2609        }
2610
2611        if (warning) {
2612            m_lastLogWarning = System.currentTimeMillis();
2613            m_warningLoggedSinceLastStatus = true;
2614            LOG.warn(
2615                Messages.get().getBundle().key(
2616                    Messages.LOG_MM_WARNING_MEM_CONSUME_2,
2617                    new Long(m_memoryCurrent.getUsage()),
2618                    new Integer(m_maxUsagePercent)));
2619        } else {
2620            m_warningLoggedSinceLastStatus = false;
2621            m_lastLogStatus = System.currentTimeMillis();
2622        }
2623
2624        if (warning) {
2625            LOG.warn(
2626                Messages.get().getBundle().key(
2627                    Messages.LOG_MM_WARNING_MEM_STATUS_6,
2628                    new Object[] {
2629                        new Long(m_memoryCurrent.getMaxMemory()),
2630                        new Long(m_memoryCurrent.getTotalMemory()),
2631                        new Long(m_memoryCurrent.getFreeMemory()),
2632                        new Long(m_memoryCurrent.getUsedMemory()),
2633                        new Long(m_memoryCurrent.getUsage()),
2634                        new Integer(m_maxUsagePercent)}));
2635        } else {
2636            m_logCount++;
2637            LOG.info(
2638                Messages.get().getBundle().key(
2639                    Messages.LOG_MM_LOG_INFO_2,
2640                    OpenCms.getSystemInfo().getServerName().toUpperCase(),
2641                    String.valueOf(m_logCount)));
2642
2643            List<String> keyList = new ArrayList<String>(m_monitoredObjects.keySet());
2644            Collections.sort(keyList);
2645            long totalSize = 0;
2646            for (Iterator<String> keys = keyList.iterator(); keys.hasNext();) {
2647                String key = keys.next();
2648                Object obj = m_monitoredObjects.get(key);
2649
2650                long size = getKeySize(obj) + getValueSize(obj) + getCosts(obj);
2651                totalSize += size;
2652
2653                PrintfFormat name1 = new PrintfFormat("%-80s");
2654                PrintfFormat name2 = new PrintfFormat("%-50s");
2655                PrintfFormat form = new PrintfFormat("%9s");
2656                LOG.info(
2657                    Messages.get().getBundle().key(
2658                        Messages.LOG_MM_NOWARN_STATUS_5,
2659                        new Object[] {
2660                            name1.sprintf(key),
2661                            name2.sprintf(obj.getClass().getName()),
2662                            form.sprintf(getItems(obj)),
2663                            form.sprintf(getLimit(obj)),
2664                            form.sprintf(Long.toString(size))}));
2665            }
2666
2667            LOG.info(
2668                Messages.get().getBundle().key(
2669                    Messages.LOG_MM_WARNING_MEM_STATUS_6,
2670                    new Object[] {
2671                        new Long(m_memoryCurrent.getMaxMemory()),
2672                        new Long(m_memoryCurrent.getTotalMemory()),
2673                        new Long(m_memoryCurrent.getFreeMemory()),
2674                        new Long(m_memoryCurrent.getUsedMemory()),
2675                        new Long(m_memoryCurrent.getUsage()),
2676                        new Integer(m_maxUsagePercent),
2677                        new Long(totalSize),
2678                        new Long(totalSize / 1048576)})
2679
2680            );
2681            LOG.info(
2682                Messages.get().getBundle().key(
2683                    Messages.LOG_MM_WARNING_MEM_STATUS_AVG_6,
2684                    new Object[] {
2685                        new Long(m_memoryAverage.getMaxMemory()),
2686                        new Long(m_memoryAverage.getTotalMemory()),
2687                        new Long(m_memoryAverage.getFreeMemory()),
2688                        new Long(m_memoryAverage.getUsedMemory()),
2689                        new Long(m_memoryAverage.getUsage()),
2690                        new Integer(m_memoryAverage.getCount())}));
2691
2692            CmsSessionManager sm = OpenCms.getSessionManager();
2693
2694            if (sm != null) {
2695                LOG.info(
2696                    Messages.get().getBundle().key(
2697                        Messages.LOG_MM_SESSION_STAT_3,
2698                        String.valueOf(sm.getSessionCountAuthenticated()),
2699                        String.valueOf(sm.getSessionCountCurrent()),
2700                        String.valueOf(sm.getSessionCountTotal())));
2701            }
2702            sm = null;
2703
2704            for (Iterator<String> i = OpenCms.getSqlManager().getDbPoolUrls().iterator(); i.hasNext();) {
2705                String poolname = i.next();
2706                try {
2707                    LOG.info(
2708                        Messages.get().getBundle().key(
2709                            Messages.LOG_MM_CONNECTIONS_3,
2710                            poolname,
2711                            Integer.toString(OpenCms.getSqlManager().getActiveConnections(poolname)),
2712                            Integer.toString(OpenCms.getSqlManager().getIdleConnections(poolname))));
2713                } catch (Exception exc) {
2714                    LOG.info(
2715                        Messages.get().getBundle().key(
2716                            Messages.LOG_MM_CONNECTIONS_3,
2717                            poolname,
2718                            Integer.toString(-1),
2719                            Integer.toString(-1)));
2720                }
2721            }
2722
2723            LOG.info(
2724                Messages.get().getBundle().key(
2725                    Messages.LOG_MM_STARTUP_TIME_2,
2726                    CmsDateUtil.getDateTimeShort(OpenCms.getSystemInfo().getStartupTime()),
2727                    CmsStringUtil.formatRuntime(OpenCms.getSystemInfo().getRuntime())));
2728        }
2729    }
2730
2731    /**
2732     * Updates the memory information of the memory monitor.<p>
2733     */
2734    protected void updateStatus() {
2735
2736        m_memoryCurrent.update();
2737        m_memoryAverage.calculateAverage(m_memoryCurrent);
2738    }
2739}