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