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