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.module; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsProject; 032import org.opencms.file.CmsResource; 033import org.opencms.file.CmsResourceFilter; 034import org.opencms.file.CmsVfsResourceNotFoundException; 035import org.opencms.importexport.CmsImportVersion10; 036import org.opencms.main.CmsException; 037import org.opencms.main.CmsLog; 038import org.opencms.main.OpenCms; 039import org.opencms.util.CmsStringUtil; 040import org.opencms.util.CmsUUID; 041 042import java.util.ArrayList; 043import java.util.Arrays; 044import java.util.Collections; 045import java.util.HashMap; 046import java.util.List; 047import java.util.Map; 048 049import org.apache.commons.logging.Log; 050 051/** 052 * Module data read from a module zip file.<p> 053 */ 054public class CmsModuleImportData { 055 056 /** Logger instance for this class. */ 057 private static final Log LOG = CmsLog.getLog(CmsModuleImportData.class); 058 059 /** The CMS context. */ 060 private CmsObject m_cms; 061 062 /** The map of conflicting ids (keys are structure ids from the manifest, values are structure ids from VFS). */ 063 private Map<CmsUUID, CmsUUID> m_conflictingIds = new HashMap<>(); 064 065 /** The module metadata. */ 066 private CmsModule m_module; 067 068 /** Online CMS object. */ 069 private CmsObject m_onlineCms; 070 071 /** The list of resource data for each entry in the manifest. */ 072 private List<CmsResourceImportData> m_resources = new ArrayList<>(); 073 074 /** 075 * Adds the information for a single resource.<p> 076 * 077 * @param resourceData the information for a single resource 078 */ 079 public void addResource(CmsResourceImportData resourceData) { 080 081 m_resources.add(resourceData); 082 } 083 084 /** 085 * Checks if the installed module is updatable with the version from the import zip file.<p> 086 * 087 * @param cms the current CMS context 088 * 089 * @return true if the module is updatable 090 */ 091 public boolean checkUpdatable(CmsObject cms) { 092 093 CmsModule newModule = getModule(); 094 LOG.info("Checking if module " + newModule.getName() + " is updateable"); 095 String exportVersion = newModule.getExportVersion(); 096 CmsModule installedModule = OpenCms.getModuleManager().getModule(getModule().getName()); 097 if (!CmsModuleUpdater.checkCompatibleModuleResources(installedModule, newModule)) { 098 LOG.info("Module is not updateable because of incompatible module resources. "); 099 return false; 100 } 101 102 if ((exportVersion == null) || !exportVersion.equals("" + CmsImportVersion10.IMPORT_VERSION10)) { 103 LOG.info("Module is not updateable because the export version is not 10."); 104 return false; 105 } 106 107 try { 108 Map<CmsUUID, CmsResourceImportData> importResourcesById = new HashMap<>(); 109 for (CmsResourceImportData resData : getResourceData()) { 110 if (resData.hasStructureId()) { 111 importResourcesById.put(resData.getResource().getStructureId(), resData); 112 } 113 } 114 cms = getCms(); 115 CmsObject onlineCms = OpenCms.initCmsObject(cms); 116 m_onlineCms = onlineCms; 117 CmsProject online = cms.readProject(CmsProject.ONLINE_PROJECT_NAME); 118 onlineCms.getRequestContext().setCurrentProject(online); 119 for (CmsResourceImportData resData : getResourceData()) { 120 String importPath = CmsModuleUpdater.normalizePath(resData.getResource().getRootPath()); 121 if (resData.hasStructureId()) { 122 CmsUUID importId = resData.getResource().getStructureId(); 123 for (CmsObject cmsToRead : Arrays.asList(cms, onlineCms)) { 124 try { 125 CmsResource resourceFromVfs = cmsToRead.readResource(importPath, CmsResourceFilter.ALL); 126 boolean skipResourceIdCheck = false; 127 if (!resourceFromVfs.getStructureId().equals(importId)) { 128 129 if (resData.getResource().isFile() 130 && resourceFromVfs.isFile() 131 && !containsStructureId(resourceFromVfs.getStructureId()) 132 && !vfsResourceWithStructureId(importId)) { 133 134 LOG.info( 135 "Permitting conflicting id in module update for resource " 136 + resData.getPath() 137 + " because the id from the module is not present in the VFS and vice versa."); 138 m_conflictingIds.put(importId, resourceFromVfs.getStructureId()); 139 140 // If we have different structure ids, but they don't conflict with anything else in the manifest or VFS, 141 // we don't compare resource ids. First, because having different resource ids is normal in this scenario, second 142 // because the resource in the VFS is deleted anyway during the module update. 143 skipResourceIdCheck = true; 144 145 } else { 146 147 LOG.info( 148 "Module is not updateable because the same path is mapped to different structure ids in the import and in the VFS: " 149 + importPath); 150 return false; 151 } 152 } 153 if (!skipResourceIdCheck 154 && resData.getResource().isFile() 155 && !(resData.getResource().getResourceId().equals(resourceFromVfs.getResourceId()))) { 156 LOG.info( 157 "Module is not updateable because of a resource id conflict for " 158 + resData.getResource().getRootPath()); 159 return false; 160 } 161 } catch (CmsVfsResourceNotFoundException e) { 162 // ignore 163 } 164 } 165 166 try { 167 CmsResource vfsResource = cms.readResource(importId, CmsResourceFilter.ALL); 168 if (vfsResource.isFolder() != resData.getResource().isFolder()) { 169 LOG.info( 170 "Module is not updateable because the same id belongs to a file in the import and a folder in the VFS, or vice versa"); 171 return false; 172 } 173 } catch (CmsVfsResourceNotFoundException e) { 174 // ignore 175 } 176 } else { 177 CmsModule module = getModule(); 178 boolean included = false; 179 boolean excluded = false; 180 for (String res : module.getResources()) { 181 if (CmsStringUtil.isPrefixPath(res, resData.getPath())) { 182 included = true; 183 break; 184 } 185 } 186 for (String res : module.getExcludeResources()) { 187 if (CmsStringUtil.isPrefixPath(res, resData.getPath())) { 188 excluded = true; 189 break; 190 } 191 } 192 if (included && !excluded) { 193 LOG.info( 194 "Module is not updateable because one of the resource entries included in the module resources has no structure id in the manifest."); 195 return false; 196 } 197 } 198 199 } 200 return true; 201 } catch (CmsException e) { 202 LOG.info("Module is not updateable because of error: " + e.getLocalizedMessage(), e); 203 return false; 204 } 205 } 206 207 /** 208 * Gets the CMS object.<p> 209 * 210 * @return the CMS object 211 */ 212 public CmsObject getCms() { 213 214 return m_cms; 215 } 216 217 /** 218 * Gets the map of conflicting ids.<p> 219 * 220 * The keys are structure ids from the manifest, the values are structure ids from the VFS. 221 * 222 * @return the conflicting id map 223 */ 224 public Map<CmsUUID, CmsUUID> getConflictingIds() { 225 226 return m_conflictingIds; 227 } 228 229 /** 230 * Gets the module metadata from the import zip.<p> 231 * 232 * @return the module metadata 233 */ 234 public CmsModule getModule() { 235 236 return m_module; 237 } 238 239 /** 240 * Gets the list of resource data objects for the manifest entries.<p> 241 * 242 * @return the resource data objects 243 */ 244 public List<CmsResourceImportData> getResourceData() { 245 246 return Collections.unmodifiableList(m_resources); 247 } 248 249 /** 250 * Sets the CMS object.<p> 251 * 252 * @param cms the CMS object to set 253 */ 254 public void setCms(CmsObject cms) { 255 256 m_cms = cms; 257 } 258 259 /** 260 * Sets the module metadata.<p> 261 * 262 * @param module the module metadata 263 */ 264 public void setModule(CmsModule module) { 265 266 m_module = module; 267 } 268 269 /** 270 * Check if the module data contains a given structure id.<p> 271 * 272 * @param structureId a structure id 273 * @return true if the module contains the given structure id 274 * 275 */ 276 private boolean containsStructureId(CmsUUID structureId) { 277 278 for (CmsResourceImportData res : m_resources) { 279 if (res.getResource().getStructureId().equals(structureId)) { 280 return true; 281 } 282 } 283 return false; 284 } 285 286 /** 287 * Checks if a resource with a given structure id exists in the VFS, either online or offline.<p> 288 * 289 * @param importId the structure id to check 290 * 291 * @return true if the VFS contains a resource with the given id 292 */ 293 private boolean vfsResourceWithStructureId(CmsUUID importId) { 294 295 return m_cms.existsResource(importId, CmsResourceFilter.ALL) 296 || m_onlineCms.existsResource(importId, CmsResourceFilter.ALL); 297 } 298 299}