001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://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.cache; 029 030import org.opencms.util.CmsFileUtil; 031import org.opencms.util.CmsStringUtil; 032 033import java.io.File; 034import java.io.FileOutputStream; 035import java.io.IOException; 036import java.io.InputStream; 037 038/** 039 * Implements a RFS file based disk cache, that handles parameter based versions of VFS files, 040 * providing a cache for the "online" and another for the "offline" project.<p> 041 * 042 * @since 6.2.0 043 */ 044public class CmsVfsDiskCache { 045 046 /** The name of the cache base repository folder in the RFS. */ 047 private String m_rfsRepository; 048 049 /** 050 * Creates a new disk cache.<p> 051 * 052 * @param basepath the base path for the cache in the RFS 053 * @param foldername the folder name for this cache, to be used a subfolder for the base folder 054 */ 055 public CmsVfsDiskCache(String basepath, String foldername) { 056 057 // normalize the given folder name 058 m_rfsRepository = CmsFileUtil.normalizePath(basepath + foldername + File.separatorChar); 059 } 060 061 /** 062 * Saves the given file content to a RFS file of the given name (full path).<p> 063 * 064 * If the required parent folders do not exists, they are also created.<p> 065 * 066 * @param rfsName the RFS name of the file to save the content in 067 * @param content the content of the file to save 068 * 069 * @return a reference to the File that was saved 070 * 071 * @throws IOException in case of disk access errors 072 */ 073 public static File saveFile(String rfsName, byte[] content) throws IOException { 074 075 File f = createFile(rfsName); 076 // write file contents 077 FileOutputStream fs = new FileOutputStream(f); 078 fs.write(content); 079 fs.close(); 080 return f; 081 } 082 083 /** 084 * Saves the given input stream content to a RFS file of the given name (full path).<p> 085 * 086 * If the required parent folders do not exists, they are also created.<p> 087 * 088 * @param rfsName the RFS name of the file to save the content in 089 * @param content the input stream to save 090 * 091 * @return a reference to the File that was saved 092 * 093 * @throws IOException in case of disk access errors 094 */ 095 public static File saveFile(String rfsName, InputStream content) throws IOException { 096 097 File f = createFile(rfsName); 098 FileOutputStream fs = new FileOutputStream(f); 099 try { 100 byte[] buffer = new byte[8192]; 101 int read; 102 while ((read = content.read(buffer)) >= 0) { 103 if (read > 0) { 104 fs.write(buffer, 0, read); 105 } 106 } 107 } finally { 108 fs.close(); 109 } 110 return f; 111 } 112 113 /** 114 * Creates a RFS file reference and its parent folders.<p> 115 * 116 * @param rfsName the RFS name of the file 117 * 118 * @return the file 119 */ 120 static File createFile(String rfsName) { 121 122 File f = new File(rfsName); 123 ensureParentFolder(f); 124 return f; 125 } 126 127 /** 128 * Ensures that the parent folder of the given file exists.<p> 129 * 130 * @param file the file 131 */ 132 private static void ensureParentFolder(File file) { 133 134 File p = file.getParentFile(); 135 if (!p.exists()) { 136 // create parent folders 137 p.mkdirs(); 138 } 139 } 140 141 /** 142 * Returns the content of the requested file in the disk cache, or <code>null</code> if the 143 * file is not found in the cache, or is found but outdated.<p> 144 * 145 * @param rfsName the file RFS name to look up in the cache 146 * @param dateLastModified the date of last modification for the cache 147 * 148 * @return the content of the requested file in the VFS disk cache, or <code>null</code> 149 */ 150 public byte[] getCacheContent(String rfsName, long dateLastModified) { 151 152 dateLastModified = simplifyDateLastModified(dateLastModified); 153 try { 154 File f = new File(rfsName); 155 if (f.exists()) { 156 if (f.lastModified() != dateLastModified) { 157 // last modification time different, remove cached file in RFS 158 f.delete(); 159 } else { 160 return CmsFileUtil.readFile(f); 161 } 162 } 163 } catch (IOException e) { 164 // unable to read content 165 } 166 return null; 167 } 168 169 /** 170 * Returns the RFS name to use for caching the given VFS resource with parameters in the disk cache.<p> 171 * 172 * @param online if true, the online disk cache is used, the offline disk cache otherwise 173 * @param rootPath the VFS resource root path to get the RFS cache name for 174 * @param parameters the parameters of the request to the VFS resource 175 * 176 * @return the RFS name to use for caching the given VFS resource with parameters 177 */ 178 public String getCacheName(boolean online, String rootPath, String parameters) { 179 180 String rfsName = CmsFileUtil.getRepositoryName(m_rfsRepository, rootPath, online); 181 if (CmsStringUtil.isNotEmpty(parameters)) { 182 String extension = CmsFileUtil.getExtension(rfsName); 183 // build the RFS name for the VFS name with parameters 184 rfsName = CmsFileUtil.getRfsPath(rfsName, extension, parameters); 185 } 186 187 return rfsName; 188 } 189 190 /** 191 * Returns the absolute path of the cache repository in the RFS.<p> 192 * 193 * @return the absolute path of the cache repository in the RFS 194 */ 195 public String getRepositoryPath() { 196 197 return m_rfsRepository; 198 } 199 200 /** 201 * Saves the given file content in the disk cache.<p> 202 * 203 * @param rfsName the RFS name of the file to save the content in 204 * @param content the content of the file to save 205 * @param dateLastModified the date of last modification to set for the save file 206 * 207 * @throws IOException in case of disk access errors 208 */ 209 public void saveCacheFile(String rfsName, byte[] content, long dateLastModified) throws IOException { 210 211 dateLastModified = simplifyDateLastModified(dateLastModified); 212 File f = saveFile(rfsName, content); 213 // set last modification date 214 f.setLastModified(dateLastModified); 215 } 216 217 /** 218 * Saves the given input stream content in the disk cache.<p> 219 * 220 * @param rfsName the RFS name of the file to save the content in 221 * @param content the input stream to save 222 * @param dateLastModified the date of last modification to set for the save file 223 * 224 * @throws IOException in case of disk access errors 225 */ 226 public void saveCacheFile(String rfsName, InputStream content, long dateLastModified) throws IOException { 227 228 dateLastModified = simplifyDateLastModified(dateLastModified); 229 File f = saveFile(rfsName, content); 230 // set last modification date 231 f.setLastModified(dateLastModified); 232 } 233 234 /** 235 * Simplifies the "date last modified" from the OpenCms VFS based milliseconds 236 * to just seconds, to support file systems that don't use milliseconds.<p> 237 * 238 * @param dateLastModified the date to simplify 239 * 240 * @return the simplified date last modified 241 */ 242 private long simplifyDateLastModified(long dateLastModified) { 243 244 return dateLastModified / 1000L; 245 } 246}