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.lock; 029 030import org.opencms.file.CmsFile; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsResource; 033import org.opencms.file.CmsResourceFilter; 034import org.opencms.file.CmsUser; 035import org.opencms.gwt.Messages; 036import org.opencms.lock.CmsLockActionRecord.LockChange; 037import org.opencms.main.CmsException; 038import org.opencms.main.CmsLog; 039import org.opencms.util.CmsFileUtil; 040 041import java.io.Closeable; 042import java.util.List; 043import java.util.Map; 044 045import org.apache.commons.logging.Log; 046 047import com.google.common.collect.Maps; 048 049/** 050 * Locking utility class.<p> 051 */ 052public final class CmsLockUtil { 053 054 /** Helper to handle the lock reports together with the files. */ 055 public static final class LockedFile implements AutoCloseable { 056 057 /** The cms object used for locking, unlocking and encoding determination. */ 058 private CmsObject m_cms; 059 /** The file that was read (cached file for getFile). */ 060 private CmsFile m_file; 061 /** The lock action record from locking the file. */ 062 private CmsLockActionRecord m_lockRecord; 063 /** Flag, indicating if the file was newly created. */ 064 private boolean m_new; 065 /** The resource that was locked. */ 066 private CmsResource m_res; 067 068 /** Private constructor. 069 * @param cms the cms user context. 070 * @param resource the resource to lock and read. 071 * @throws CmsException thrown if locking fails. 072 */ 073 private LockedFile(CmsObject cms, CmsResource resource) 074 throws CmsException { 075 076 m_lockRecord = CmsLockUtil.ensureLock(cms, resource); 077 m_res = resource; 078 m_new = false; 079 m_cms = cms; 080 } 081 082 /** 083 * Lock and read a file. 084 * @param cms the cms user context. 085 * @param resource the resource to lock and read. 086 * @return the read file with the lock action record. 087 * @throws CmsException thrown if locking fails 088 */ 089 public static LockedFile lockResource(CmsObject cms, CmsResource resource) throws CmsException { 090 091 return new LockedFile(cms, resource); 092 } 093 094 /** 095 * @see java.lang.AutoCloseable#close() 096 */ 097 public void close() throws Exception { 098 099 tryUnlock(); 100 101 } 102 103 /** 104 * Returns the encoding used for the file. 105 * 106 * @see CmsFileUtil#getEncoding(CmsObject, CmsResource) 107 * 108 * @return the encoding used for the file. 109 */ 110 public String getEncoding() { 111 112 return CmsFileUtil.getEncoding(m_cms, m_res); 113 114 } 115 116 /** Returns the file, or null if reading fails. 117 * @return the file, or null if reading fails. 118 */ 119 @SuppressWarnings("synthetic-access") 120 public CmsFile getFile() { 121 122 if ((null == m_file) && m_res.isFile()) { 123 try { 124 m_file = m_cms.readFile(m_res); 125 } catch (CmsException e) { 126 LOG.error(e.getLocalizedMessage(), e); 127 } 128 } 129 return m_file; 130 } 131 132 /** Returns the lock action record. 133 * @return the lock action record. 134 */ 135 public CmsLockActionRecord getLockActionRecord() { 136 137 return m_lockRecord; 138 } 139 140 /** 141 * Returns a flag, indicating if the file is newly created. 142 * @return flag, indicating if the file is newly created. 143 */ 144 public boolean isCreated() { 145 146 return m_new; 147 } 148 149 /** 150 * Set the flag, indicating if the file was newly created. 151 * @param isNew flag, indicating if the file was newly created. 152 */ 153 public void setCreated(boolean isNew) { 154 155 m_new = isNew; 156 157 } 158 159 /** 160 * Unlocks the resource if it was not formerly locked.<p> 161 * 162 * @return <code>true</code> in case the resource was unlocked 163 */ 164 public boolean tryUnlock() { 165 166 if (!m_lockRecord.getChange().equals(LockChange.unchanged) || m_new) { 167 try { 168 m_cms.unlockResource(m_res); 169 return true; 170 } catch (CmsException e) { 171 // this will happen in case a parent folder is still locked, can be ignored 172 } 173 174 } 175 return false; 176 } 177 } 178 179 /** Logger instance for this class. */ 180 private static final Log LOG = CmsLog.getLog(CmsLockUtil.class); 181 182 /** 183 * Hidden constructor. 184 */ 185 private CmsLockUtil() { 186 // Hide constructor for util class 187 } 188 189 /** 190 * Static helper method to lock a resource.<p> 191 * 192 * @param cms the CMS context to use 193 * @param resource the resource to lock 194 * @return the action that was taken 195 * 196 * @throws CmsException if something goes wrong 197 */ 198 public static CmsLockActionRecord ensureLock(CmsObject cms, CmsResource resource) throws CmsException { 199 200 LockChange change = LockChange.unchanged; 201 List<CmsResource> blockingResources = cms.getBlockingLockedResources(resource); 202 if ((blockingResources != null) && !blockingResources.isEmpty()) { 203 throw new CmsException( 204 Messages.get().container( 205 Messages.ERR_RESOURCE_HAS_BLOCKING_LOCKED_CHILDREN_1, 206 cms.getSitePath(resource))); 207 } 208 CmsUser user = cms.getRequestContext().getCurrentUser(); 209 CmsLock lock = cms.getLock(resource); 210 if (!lock.isOwnedBy(user)) { 211 cms.lockResourceTemporary(resource); 212 change = LockChange.locked; 213 lock = cms.getLock(resource); 214 } else if (!lock.isOwnedInProjectBy(user, cms.getRequestContext().getCurrentProject())) { 215 cms.changeLock(resource); 216 change = LockChange.changed; 217 lock = cms.getLock(resource); 218 } 219 return new CmsLockActionRecord(lock, change); 220 } 221 222 /** 223 * Tries to unlock the given resource.<p> 224 * Will ignore any failure.<p> 225 * 226 * @param cms the cms context 227 * @param resource the resource to unlock 228 */ 229 public static void tryUnlock(CmsObject cms, CmsResource resource) { 230 231 try { 232 cms.unlockResource(resource); 233 } catch (CmsException e) { 234 LOG.debug("Unable to unlock " + resource.getRootPath(), e); 235 } 236 } 237 238 /** 239 * Utility method for locking and unlocking a set of resources conveniently with the try-with syntax 240 * from Java 1.7.<p> 241 * 242 * This method locks a set of resources and returns a Closeable instance that will unlock the locked resources 243 * when its close() method is called. 244 * 245 * @param cms the CMS context 246 * @param resources the resources to lock 247 * 248 * @return the Closeable used to unlock the resources 249 * @throws Exception if something goes wrong 250 */ 251 public static AutoCloseable withLockedResources(final CmsObject cms, CmsResource... resources) throws Exception { 252 253 final Map<CmsResource, CmsLockActionRecord> lockMap = Maps.newHashMap(); 254 Closeable result = new Closeable() { 255 256 @SuppressWarnings("synthetic-access") 257 public void close() { 258 259 for (Map.Entry<CmsResource, CmsLockActionRecord> entry : lockMap.entrySet()) { 260 if (entry.getValue().getChange() == LockChange.locked) { 261 CmsResource resourceToUnlock = entry.getKey(); 262 // the resource may have been moved, so we read it again to get the correct path 263 try { 264 resourceToUnlock = cms.readResource(entry.getKey().getStructureId(), CmsResourceFilter.ALL); 265 } catch (CmsException e) { 266 LOG.error(e.getLocalizedMessage(), e); 267 } 268 try { 269 cms.unlockResource(resourceToUnlock); 270 } catch (CmsException e) { 271 LOG.warn(e.getLocalizedMessage(), e); 272 } 273 } 274 275 } 276 } 277 }; 278 try { 279 for (CmsResource resource : resources) { 280 CmsLockActionRecord record = ensureLock(cms, resource); 281 lockMap.put(resource, record); 282 } 283 } catch (CmsException e) { 284 result.close(); 285 throw e; 286 } 287 return result; 288 } 289 290}