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, 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.xml.containerpage; 029 030import org.opencms.cache.CmsVfsCache; 031import org.opencms.file.CmsResource; 032import org.opencms.file.types.CmsResourceTypeXmlContainerPage; 033import org.opencms.main.CmsLog; 034import org.opencms.monitor.CmsMemoryMonitor; 035import org.opencms.util.CmsUUID; 036import org.opencms.xml.content.CmsXmlContent; 037 038import java.util.Iterator; 039import java.util.Map; 040import java.util.concurrent.locks.ReadWriteLock; 041import java.util.concurrent.locks.ReentrantReadWriteLock; 042 043import org.apache.commons.logging.Log; 044 045/** 046 * Cache object instance for simultaneously cache online and offline items.<p> 047 * 048 * @since 7.6 049 */ 050public final class CmsADECache extends CmsVfsCache { 051 052 /** The log to use (static for performance reasons).<p> */ 053 private static final Log LOG = CmsLog.getLog(CmsADECache.class); 054 055 /** Cache for offline container pages. */ 056 private Map<String, CmsXmlContainerPage> m_containerPagesOffline; 057 058 /** Cache for online container pages. */ 059 private Map<String, CmsXmlContainerPage> m_containerPagesOnline; 060 061 /** Cache for offline group containers. */ 062 private Map<String, CmsXmlGroupContainer> m_groupContainersOffline; 063 064 /** Cache for online group containers. */ 065 private Map<String, CmsXmlGroupContainer> m_groupContainersOnline; 066 067 /** Read-write lock to ensure that the cache maps aren't accessed while we iterate through them to remove invalid entries. */ 068 private ReadWriteLock m_lock = new ReentrantReadWriteLock(true); 069 070 /** 071 * Initializes the cache. Only intended to be called during startup.<p> 072 * 073 * @param memMonitor the memory monitor instance 074 * @param cacheSettings the system cache settings 075 * 076 * @see org.opencms.main.OpenCmsCore#initConfiguration 077 */ 078 public CmsADECache(CmsMemoryMonitor memMonitor, CmsADECacheSettings cacheSettings) { 079 080 initialize(memMonitor, cacheSettings); 081 registerEventListener(); 082 } 083 084 /** 085 * Flushes the container pages cache.<p> 086 * 087 * @param online if to flush the online or offline cache 088 */ 089 public void flushContainerPages(boolean online) { 090 091 try { 092 m_lock.writeLock().lock(); 093 if (online) { 094 m_containerPagesOnline.clear(); 095 } else { 096 m_containerPagesOffline.clear(); 097 } 098 } finally { 099 m_lock.writeLock().unlock(); 100 } 101 } 102 103 /** 104 * Flushes the group containers cache.<p> 105 * 106 * @param online if to flush the online or offline cache 107 */ 108 public void flushGroupContainers(boolean online) { 109 110 try { 111 m_lock.writeLock().lock(); 112 if (online) { 113 m_groupContainersOnline.clear(); 114 } else { 115 m_groupContainersOffline.clear(); 116 } 117 } finally { 118 m_lock.writeLock().unlock(); 119 } 120 } 121 122 /** 123 * Returns the cached container page under the given key and for the given project.<p> 124 * 125 * @param key the cache key 126 * @param online if cached in online or offline project 127 * 128 * @return the cached container page or <code>null</code> if not found 129 */ 130 public CmsXmlContainerPage getCacheContainerPage(String key, boolean online) { 131 132 try { 133 m_lock.readLock().lock(); 134 CmsXmlContainerPage retValue; 135 if (online) { 136 retValue = m_containerPagesOnline.get(key); 137 if (LOG.isDebugEnabled()) { 138 if (retValue == null) { 139 LOG.debug( 140 Messages.get().getBundle().key( 141 Messages.LOG_DEBUG_CACHE_MISSED_ONLINE_1, 142 new Object[] {key})); 143 144 } else { 145 LOG.debug( 146 Messages.get().getBundle().key( 147 Messages.LOG_DEBUG_CACHE_MATCHED_ONLINE_2, 148 new Object[] {key, retValue})); 149 } 150 } 151 } else { 152 retValue = m_containerPagesOffline.get(key); 153 if (LOG.isDebugEnabled()) { 154 if (retValue == null) { 155 LOG.debug( 156 Messages.get().getBundle().key( 157 Messages.LOG_DEBUG_CACHE_MISSED_OFFLINE_1, 158 new Object[] {key})); 159 160 } else { 161 LOG.debug( 162 Messages.get().getBundle().key( 163 Messages.LOG_DEBUG_CACHE_MATCHED_OFFLINE_2, 164 new Object[] {key, retValue})); 165 } 166 } 167 } 168 if (retValue != null) { 169 //System.out.println("got cached page: " + retValue.getFile().getRootPath()); 170 } 171 return retValue; 172 } finally { 173 m_lock.readLock().unlock(); 174 } 175 } 176 177 /** 178 * Returns the cached group container under the given key and for the given project.<p> 179 * 180 * @param key the cache key 181 * @param online if cached in online or offline project 182 * 183 * @return the cached group container or <code>null</code> if not found 184 */ 185 public CmsXmlGroupContainer getCacheGroupContainer(String key, boolean online) { 186 187 try { 188 m_lock.readLock().lock(); 189 CmsXmlGroupContainer retValue; 190 if (online) { 191 retValue = m_groupContainersOnline.get(key); 192 if (LOG.isDebugEnabled()) { 193 if (retValue == null) { 194 LOG.debug( 195 Messages.get().getBundle().key( 196 Messages.LOG_DEBUG_CACHE_MISSED_ONLINE_1, 197 new Object[] {key})); 198 199 } else { 200 LOG.debug( 201 Messages.get().getBundle().key( 202 Messages.LOG_DEBUG_CACHE_MATCHED_ONLINE_2, 203 new Object[] {key, retValue})); 204 } 205 } 206 } else { 207 retValue = m_groupContainersOffline.get(key); 208 if (LOG.isDebugEnabled()) { 209 if (retValue == null) { 210 LOG.debug( 211 Messages.get().getBundle().key( 212 Messages.LOG_DEBUG_CACHE_MISSED_OFFLINE_1, 213 new Object[] {key})); 214 215 } else { 216 LOG.debug( 217 Messages.get().getBundle().key( 218 Messages.LOG_DEBUG_CACHE_MATCHED_OFFLINE_2, 219 new Object[] {key, retValue})); 220 } 221 } 222 } 223 return retValue; 224 } finally { 225 m_lock.readLock().unlock(); 226 } 227 } 228 229 /** 230 * Returns the cache key for the given parameters.<p> 231 * 232 * @param structureId the container page's structure id 233 * @param keepEncoding if to keep the encoding while unmarshalling 234 * 235 * @return the cache key for the given container page and parameters 236 */ 237 public String getCacheKey(CmsUUID structureId, boolean keepEncoding) { 238 239 return structureId.toString() + "_" + keepEncoding; 240 } 241 242 /** 243 * Caches the given container page under the given key and for the given project.<p> 244 * 245 * @param key the cache key 246 * @param containerPage the object to cache 247 * @param online if to cache in online or offline project 248 */ 249 public void setCacheContainerPage(String key, CmsXmlContainerPage containerPage, boolean online) { 250 251 try { 252 m_lock.writeLock().lock(); 253 //System.out.println("caching page:" + containerPage.getFile().getRootPath()); 254 255 if (online) { 256 m_containerPagesOnline.put(key, containerPage); 257 if (LOG.isDebugEnabled()) { 258 LOG.debug( 259 Messages.get().getBundle().key( 260 Messages.LOG_DEBUG_CACHE_SET_ONLINE_2, 261 new Object[] {key, containerPage})); 262 } 263 } else { 264 m_containerPagesOffline.put(key, containerPage); 265 if (LOG.isDebugEnabled()) { 266 LOG.debug( 267 Messages.get().getBundle().key( 268 Messages.LOG_DEBUG_CACHE_SET_OFFLINE_2, 269 new Object[] {key, containerPage})); 270 } 271 } 272 } finally { 273 m_lock.writeLock().unlock(); 274 } 275 } 276 277 /** 278 * Caches the given group container under the given key and for the given project.<p> 279 * 280 * @param key the cache key 281 * @param groupContainer the object to cache 282 * @param online if to cache in online or offline project 283 */ 284 public void setCacheGroupContainer(String key, CmsXmlGroupContainer groupContainer, boolean online) { 285 286 try { 287 m_lock.writeLock().lock(); 288 if (online) { 289 m_groupContainersOnline.put(key, groupContainer); 290 if (LOG.isDebugEnabled()) { 291 LOG.debug( 292 Messages.get().getBundle().key( 293 Messages.LOG_DEBUG_CACHE_SET_ONLINE_2, 294 new Object[] {key, groupContainer})); 295 } 296 } else { 297 m_groupContainersOffline.put(key, groupContainer); 298 if (LOG.isDebugEnabled()) { 299 LOG.debug( 300 Messages.get().getBundle().key( 301 Messages.LOG_DEBUG_CACHE_SET_OFFLINE_2, 302 new Object[] {key, groupContainer})); 303 } 304 } 305 } finally { 306 m_lock.writeLock().unlock(); 307 } 308 } 309 310 /** 311 * Removes the container page identified by its structure id from the cache.<p> 312 * 313 * @param structureId the container page's structure id 314 * @param online if online or offline 315 */ 316 public void uncacheContainerPage(CmsUUID structureId, boolean online) { 317 318 try { 319 m_lock.writeLock().lock(); 320 if (online) { 321 m_containerPagesOnline.remove(getCacheKey(structureId, true)); 322 m_containerPagesOnline.remove(getCacheKey(structureId, false)); 323 } else { 324 m_containerPagesOffline.remove(getCacheKey(structureId, true)); 325 m_containerPagesOffline.remove(getCacheKey(structureId, false)); 326 } 327 } finally { 328 m_lock.writeLock().unlock(); 329 } 330 } 331 332 /** 333 * Removes the group container identified by its structure id from the cache.<p> 334 * 335 * @param structureId the group container's structure id 336 * @param online if online or offline 337 */ 338 public void uncacheGroupContainer(CmsUUID structureId, boolean online) { 339 340 try { 341 m_lock.writeLock().lock(); 342 if (online) { 343 m_groupContainersOnline.remove(getCacheKey(structureId, true)); 344 m_groupContainersOnline.remove(getCacheKey(structureId, false)); 345 } else { 346 m_groupContainersOffline.remove(getCacheKey(structureId, true)); 347 m_groupContainersOffline.remove(getCacheKey(structureId, false)); 348 } 349 } finally { 350 m_lock.writeLock().unlock(); 351 } 352 } 353 354 /** 355 * @see org.opencms.cache.CmsVfsCache#flush(boolean) 356 */ 357 @Override 358 protected void flush(boolean online) { 359 360 try { 361 m_lock.writeLock().lock(); 362 flushContainerPages(online); 363 flushGroupContainers(online); 364 } finally { 365 m_lock.writeLock().unlock(); 366 } 367 } 368 369 /** 370 * @see org.opencms.cache.CmsVfsCache#uncacheResource(org.opencms.file.CmsResource) 371 */ 372 @Override 373 protected void uncacheResource(CmsResource resource) { 374 375 try { 376 m_lock.writeLock().lock(); 377 if (resource == null) { 378 LOG.warn(Messages.get().container(Messages.LOG_WARN_UNCACHE_NULL_0)); 379 return; 380 } 381 if (CmsResourceTypeXmlContainerPage.isContainerPage(resource)) { 382 removeCachedContent(resource, m_containerPagesOffline); 383 } else { 384 removeCachedContent(resource, m_groupContainersOffline); 385 } 386 } finally { 387 m_lock.writeLock().unlock(); 388 } 389 } 390 391 /** 392 * Initializes the caches.<p> 393 * 394 * @param memMonitor the memory monitor instance 395 * @param cacheSettings the system cache settings 396 */ 397 private void initialize(CmsMemoryMonitor memMonitor, CmsADECacheSettings cacheSettings) { 398 399 // container page caches 400 m_containerPagesOffline = CmsMemoryMonitor.createLRUCacheMap(cacheSettings.getContainerPageOfflineSize()); 401 memMonitor.register(CmsADECache.class.getName() + ".containerPagesOffline", m_containerPagesOffline); 402 403 m_containerPagesOnline = CmsMemoryMonitor.createLRUCacheMap(cacheSettings.getContainerPageOnlineSize()); 404 memMonitor.register(CmsADECache.class.getName() + ".containerPagesOnline", m_containerPagesOnline); 405 406 // container page caches 407 m_groupContainersOffline = CmsMemoryMonitor.createLRUCacheMap(cacheSettings.getGroupContainerOfflineSize()); 408 memMonitor.register(CmsADECache.class.getName() + ".groupContainersOffline", m_groupContainersOffline); 409 410 m_groupContainersOnline = CmsMemoryMonitor.createLRUCacheMap(cacheSettings.getGroupContainerOnlineSize()); 411 memMonitor.register(CmsADECache.class.getName() + ".groupContainersOnline", m_groupContainersOnline); 412 } 413 414 /** 415 * Removes a cached XML content from the cache if it matches a given resource.<p> 416 * 417 * @param resource the resource for which the cached XML content should be removed 418 * @param cache the cache from which to remove the XML content 419 */ 420 private <CONTENT extends CmsXmlContent> void removeCachedContent(CmsResource resource, Map<String, CONTENT> cache) { 421 422 Iterator<Map.Entry<String, CONTENT>> iterator = cache.entrySet().iterator(); 423 while (iterator.hasNext()) { 424 Map.Entry<String, CONTENT> entry = iterator.next(); 425 CONTENT content = entry.getValue(); 426 CmsResource contentFile = content.getFile(); 427 if (contentFile.getStructureId().equals(resource.getStructureId()) 428 || contentFile.getResourceId().equals(resource.getResourceId())) { 429 iterator.remove(); 430 } 431 } 432 433 } 434}