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.ade.configuration; 029 030import org.opencms.db.CmsPublishedResource; 031import org.opencms.db.urlname.CmsUrlNameMappingEntry; 032import org.opencms.db.urlname.CmsUrlNameMappingFilter; 033import org.opencms.file.CmsObject; 034import org.opencms.file.CmsResource; 035import org.opencms.file.types.CmsResourceTypeXmlContainerPage; 036import org.opencms.file.types.CmsResourceTypeXmlContent; 037import org.opencms.file.types.I_CmsResourceType; 038import org.opencms.loader.CmsLoaderException; 039import org.opencms.main.CmsLog; 040import org.opencms.main.OpenCms; 041import org.opencms.util.CmsManyToOneMap; 042import org.opencms.util.CmsUUID; 043 044import java.util.Collections; 045import java.util.HashSet; 046import java.util.List; 047import java.util.Set; 048import java.util.concurrent.TimeUnit; 049 050import org.apache.commons.logging.Log; 051 052import com.google.common.collect.Sets; 053 054/** 055 * A cache which stores structure ids for URL names.<p> 056 */ 057public class CmsDetailNameCache implements I_CmsGlobalConfigurationCache { 058 059 /** The delay between updates. */ 060 public static final int DELAY_MILLIS = 10000; 061 062 /** The logger for this class. */ 063 private static final Log LOG = CmsLog.getLog(CmsDetailNameCache.class); 064 065 /** The CMS context used by this cache. */ 066 private CmsObject m_cms; 067 068 /** The internal map from URL names to structure ids. */ 069 private CmsManyToOneMap<String, CmsUUID> m_detailIdCache = new CmsManyToOneMap<String, CmsUUID>(); 070 071 /** The set of structure ids for which the URL names have to be updated. */ 072 private Set<CmsUUID> m_updateSet = Sets.newHashSet(); 073 074 /** 075 * Creates a new instance.<p> 076 * 077 * @param cms the CMS context to use 078 */ 079 public CmsDetailNameCache(CmsObject cms) { 080 081 m_cms = cms; 082 } 083 084 /** 085 * @see org.opencms.ade.configuration.I_CmsGlobalConfigurationCache#clear() 086 */ 087 public void clear() { 088 089 markForUpdate(CmsUUID.getNullUUID()); 090 091 } 092 093 /** 094 * Gets the structure id for a given URL name.<p> 095 * 096 * @param name the URL name 097 * @return the structure id for the URL name 098 */ 099 public CmsUUID getDetailId(String name) { 100 101 return m_detailIdCache.get(name); 102 } 103 104 /** 105 * Initializes the cache by scheduling the update actions and loading the initial cache contents.<p> 106 */ 107 public void initialize() { 108 109 OpenCms.getExecutor().scheduleWithFixedDelay(new Runnable() { 110 111 public void run() { 112 113 checkForUpdates(); 114 } 115 }, DELAY_MILLIS, DELAY_MILLIS, TimeUnit.MILLISECONDS); 116 reload(); 117 } 118 119 /** 120 * @see org.opencms.ade.configuration.I_CmsGlobalConfigurationCache#remove(org.opencms.db.CmsPublishedResource) 121 */ 122 public void remove(CmsPublishedResource pubRes) { 123 124 checkIfUpdateIsNeeded(pubRes.getStructureId(), pubRes.getRootPath(), pubRes.getType()); 125 } 126 127 /** 128 * @see org.opencms.ade.configuration.I_CmsGlobalConfigurationCache#remove(org.opencms.file.CmsResource) 129 */ 130 public void remove(CmsResource resource) { 131 132 checkIfUpdateIsNeeded(resource.getStructureId(), resource.getRootPath(), resource.getTypeId()); 133 134 } 135 136 /** 137 * @see org.opencms.ade.configuration.I_CmsGlobalConfigurationCache#update(org.opencms.db.CmsPublishedResource) 138 */ 139 public void update(CmsPublishedResource pubRes) { 140 141 checkIfUpdateIsNeeded(pubRes.getStructureId(), pubRes.getRootPath(), pubRes.getType()); 142 143 } 144 145 /** 146 * @see org.opencms.ade.configuration.I_CmsGlobalConfigurationCache#update(org.opencms.file.CmsResource) 147 */ 148 public void update(CmsResource resource) { 149 150 checkIfUpdateIsNeeded(resource.getStructureId(), resource.getRootPath(), resource.getTypeId()); 151 152 } 153 154 /** 155 * Checks if any updates are necessary and if so, performs them.<p> 156 */ 157 synchronized void checkForUpdates() { 158 159 if (!m_updateSet.isEmpty()) { 160 Set<CmsUUID> copiedIds = Sets.newHashSet(m_updateSet); 161 m_updateSet.clear(); 162 163 if (copiedIds.contains(CmsUUID.getNullUUID())) { 164 LOG.info("Updating detail name cache: reloading..."); 165 reload(); 166 } else { 167 LOG.info("Updating detail name cache. Number of changed files: " + copiedIds.size()); 168 CmsManyToOneMap<String, CmsUUID> cacheCopy = new CmsManyToOneMap<String, CmsUUID>(m_detailIdCache); 169 for (CmsUUID id : copiedIds) { 170 Set<String> urlNames = getUrlNames(id); 171 cacheCopy.removeValue(id); 172 for (String urlName : urlNames) { 173 cacheCopy.put(urlName, id); 174 } 175 } 176 m_detailIdCache = cacheCopy; 177 } 178 } 179 } 180 181 /** 182 * Checks if the cache needs to be updated for the resource, and if so, marks the structure id for updating.<p> 183 * 184 * @param structureId the structure id for the resource 185 * @param rootPath the path of the resource 186 * @param typeId the resource type id 187 */ 188 private void checkIfUpdateIsNeeded(CmsUUID structureId, String rootPath, int typeId) { 189 190 try { 191 I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(typeId); 192 if ((resType instanceof CmsResourceTypeXmlContent) 193 && !OpenCms.getResourceManager().matchResourceType( 194 CmsResourceTypeXmlContainerPage.RESOURCE_TYPE_NAME, 195 typeId)) { 196 markForUpdate(structureId); 197 } 198 } catch (CmsLoaderException e) { 199 // resource type not found, just log an error 200 LOG.error(e.getLocalizedMessage(), e); 201 } 202 } 203 204 /** 205 * Reads the URL names for the id.<p> 206 * 207 * @param id the structure id of a resource 208 * @return the URL names for the resource 209 */ 210 private Set<String> getUrlNames(CmsUUID id) { 211 212 try { 213 return new HashSet<String>(m_cms.readUrlNamesForAllLocales(id)); 214 } catch (Exception e) { 215 LOG.error(e.getLocalizedMessage(), e); 216 return Collections.emptySet(); 217 } 218 } 219 220 /** 221 * Marks the structure id for updating.<p> 222 * 223 * @param id the structure id to update 224 */ 225 private synchronized void markForUpdate(CmsUUID id) { 226 227 m_updateSet.add(id); 228 } 229 230 /** 231 * Loads the complete URL name data into the cache.<p> 232 */ 233 private void reload() { 234 235 CmsManyToOneMap<String, CmsUUID> newMap = new CmsManyToOneMap<String, CmsUUID>(); 236 try { 237 List<CmsUrlNameMappingEntry> mappings = m_cms.readUrlNameMappings(CmsUrlNameMappingFilter.ALL); 238 LOG.info("Initializing detail name cache with " + mappings.size() + " entries"); 239 for (CmsUrlNameMappingEntry entry : mappings) { 240 newMap.put(entry.getName(), entry.getStructureId()); 241 } 242 m_detailIdCache = newMap; 243 } catch (Exception e) { 244 LOG.error(e.getLocalizedMessage(), e); 245 } 246 } 247}