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 { 056 057 /** The cms object used for locking, unlocking and encoding determination. */ 058 private CmsObject m_cms; 059 /** The file that was read. */ 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 066 /** Private constructor. 067 * @param cms the cms user context. 068 * @param resource the resource to lock and read. 069 * @throws CmsException thrown if locking fails. 070 */ 071 private LockedFile(CmsObject cms, CmsResource resource) 072 throws CmsException { 073 m_lockRecord = CmsLockUtil.ensureLock(cms, resource); 074 m_file = cms.readFile(resource); 075 m_new = false; 076 m_cms = cms; 077 } 078 079 /** 080 * Lock and read a file. 081 * @param cms the cms user context. 082 * @param resource the resource to lock and read. 083 * @return the read file with the lock action record. 084 * @throws CmsException thrown if locking fails 085 */ 086 public static LockedFile lockResource(CmsObject cms, CmsResource resource) throws CmsException { 087 088 return new LockedFile(cms, resource); 089 } 090 091 /** 092 * Returns the encoding used for the file. 093 * 094 * @see CmsFileUtil#getEncoding(CmsObject, CmsResource) 095 * 096 * @return the encoding used for the file. 097 */ 098 public String getEncoding() { 099 100 return CmsFileUtil.getEncoding(m_cms, m_file); 101 102 } 103 104 /** Returns the file. 105 * @return the file. 106 */ 107 public CmsFile getFile() { 108 109 return m_file; 110 } 111 112 /** Returns the lock action record. 113 * @return the lock action record. 114 */ 115 public CmsLockActionRecord getLockActionRecord() { 116 117 return m_lockRecord; 118 } 119 120 /** 121 * Returns a flag, indicating if the file is newly created. 122 * @return flag, indicating if the file is newly created. 123 */ 124 public boolean isCreated() { 125 126 return m_new; 127 } 128 129 /** 130 * Set the flag, indicating if the file was newly created. 131 * @param isNew flag, indicating if the file was newly created. 132 */ 133 public void setCreated(boolean isNew) { 134 135 m_new = isNew; 136 137 } 138 139 public boolean unlock() throws CmsException { 140 141 if (!m_lockRecord.getChange().equals(LockChange.unchanged) || m_new) { 142 m_cms.unlockResource(m_file); 143 return true; 144 } else { 145 return false; 146 } 147 } 148 149 } 150 151 /** Logger instance for this class. */ 152 private static final Log LOG = CmsLog.getLog(CmsLockUtil.class); 153 154 /** 155 * Hidden constructor. 156 */ 157 private CmsLockUtil() { 158 // Hide constructor for util class 159 } 160 161 /** 162 * Static helper method to lock a resource.<p> 163 * 164 * @param cms the CMS context to use 165 * @param resource the resource to lock 166 * @return the action that was taken 167 * 168 * @throws CmsException if something goes wrong 169 */ 170 public static CmsLockActionRecord ensureLock(CmsObject cms, CmsResource resource) throws CmsException { 171 172 LockChange change = LockChange.unchanged; 173 List<CmsResource> blockingResources = cms.getBlockingLockedResources(resource); 174 if ((blockingResources != null) && !blockingResources.isEmpty()) { 175 throw new CmsException( 176 Messages.get().container( 177 Messages.ERR_RESOURCE_HAS_BLOCKING_LOCKED_CHILDREN_1, 178 cms.getSitePath(resource))); 179 } 180 CmsUser user = cms.getRequestContext().getCurrentUser(); 181 CmsLock lock = cms.getLock(resource); 182 if (!lock.isOwnedBy(user)) { 183 cms.lockResourceTemporary(resource); 184 change = LockChange.locked; 185 lock = cms.getLock(resource); 186 } else if (!lock.isOwnedInProjectBy(user, cms.getRequestContext().getCurrentProject())) { 187 cms.changeLock(resource); 188 change = LockChange.changed; 189 lock = cms.getLock(resource); 190 } 191 return new CmsLockActionRecord(lock, change); 192 } 193 194 /** 195 * Utility method for locking and unlocking a set of resources conveniently with the try-with syntax 196 * from Java 1.7.<p> 197 * 198 * This method locks a set of resources and returns a Closeable instance that will unlock the locked resources 199 * when its close() method is called. 200 * 201 * @param cms the CMS context 202 * @param resources the resources to lock 203 * 204 * @return the Closeable used to unlock the resources 205 * @throws Exception if something goes wrong 206 */ 207 public static Closeable withLockedResources(final CmsObject cms, CmsResource... resources) throws Exception { 208 209 final Map<CmsResource, CmsLockActionRecord> lockMap = Maps.newHashMap(); 210 Closeable result = new Closeable() { 211 212 @SuppressWarnings("synthetic-access") 213 public void close() { 214 215 for (Map.Entry<CmsResource, CmsLockActionRecord> entry : lockMap.entrySet()) { 216 if (entry.getValue().getChange() == LockChange.locked) { 217 CmsResource resourceToUnlock = entry.getKey(); 218 // the resource may have been moved, so we read it again to get the correct path 219 try { 220 resourceToUnlock = cms.readResource(entry.getKey().getStructureId(), CmsResourceFilter.ALL); 221 } catch (CmsException e) { 222 LOG.error(e.getLocalizedMessage(), e); 223 } 224 try { 225 cms.unlockResource(resourceToUnlock); 226 } catch (CmsException e) { 227 LOG.warn(e.getLocalizedMessage(), e); 228 } 229 } 230 231 } 232 } 233 }; 234 try { 235 for (CmsResource resource : resources) { 236 CmsLockActionRecord record = ensureLock(cms, resource); 237 lockMap.put(resource, record); 238 } 239 } catch (CmsException e) { 240 result.close(); 241 throw e; 242 } 243 return result; 244 } 245 246}