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.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;
036
037/**
038 * Implements a RFS file based disk cache, that handles parameter based versions of VFS files,
039 * providing a cache for the "online" and another for the "offline" project.<p>
040 *
041 * @since 6.2.0
042 */
043public class CmsVfsDiskCache {
044
045    /** The name of the cache base repository folder in the RFS. */
046    private String m_rfsRepository;
047
048    /**
049     * Creates a new disk cache.<p>
050     *
051     * @param basepath the base path for the cache in the RFS
052     * @param foldername the folder name for this cache, to be used a subfolder for the base folder
053     */
054    public CmsVfsDiskCache(String basepath, String foldername) {
055
056        // normalize the given folder name
057        m_rfsRepository = CmsFileUtil.normalizePath(basepath + foldername + File.separatorChar);
058    }
059
060    /**
061     * Saves the given file content to a RFS file of the given name (full path).<p>
062     *
063     * If the required parent folders do not exists, they are also created.<p>
064     *
065     * @param rfsName the RFS name of the file to save the content in
066     * @param content the content of the file to save
067     *
068     * @return a reference to the File that was saved
069     *
070     * @throws IOException in case of disk access errors
071     */
072    public static File saveFile(String rfsName, byte[] content) throws IOException {
073
074        File f = new File(rfsName);
075        File p = f.getParentFile();
076        if (!p.exists()) {
077            // create parent folders
078            p.mkdirs();
079        }
080        // write file contents
081        FileOutputStream fs = new FileOutputStream(f);
082        fs.write(content);
083        fs.close();
084        return f;
085    }
086
087    /**
088     * Returns the content of the requested file in the disk cache, or <code>null</code> if the
089     * file is not found in the cache, or is found but outdated.<p>
090     *
091     * @param rfsName the file RFS name to look up in the cache
092     * @param dateLastModified the date of last modification for the cache
093     *
094     * @return the content of the requested file in the VFS disk cache, or <code>null</code>
095     */
096    public byte[] getCacheContent(String rfsName, long dateLastModified) {
097
098        dateLastModified = simplifyDateLastModified(dateLastModified);
099        try {
100            File f = new File(rfsName);
101            if (f.exists()) {
102                if (f.lastModified() != dateLastModified) {
103                    // last modification time different, remove cached file in RFS
104                    f.delete();
105                } else {
106                    return CmsFileUtil.readFile(f);
107                }
108            }
109        } catch (IOException e) {
110            // unable to read content
111        }
112        return null;
113    }
114
115    /**
116     * Returns the RFS name to use for caching the given VFS resource with parameters in the disk cache.<p>
117     *
118     * @param online if true, the online disk cache is used, the offline disk cache otherwise
119     * @param rootPath the VFS resource root path to get the RFS cache name for
120     * @param parameters the parameters of the request to the VFS resource
121     *
122     * @return the RFS name to use for caching the given VFS resource with parameters
123     */
124    public String getCacheName(boolean online, String rootPath, String parameters) {
125
126        String rfsName = CmsFileUtil.getRepositoryName(m_rfsRepository, rootPath, online);
127        if (CmsStringUtil.isNotEmpty(parameters)) {
128            String extension = CmsFileUtil.getExtension(rfsName);
129            // build the RFS name for the VFS name with parameters
130            rfsName = CmsFileUtil.getRfsPath(rfsName, extension, parameters);
131        }
132
133        return rfsName;
134    }
135
136    /**
137     * Returns the absolute path of the cache repository in the RFS.<p>
138     *
139     * @return the absolute path of the cache repository in the RFS
140     */
141    public String getRepositoryPath() {
142
143        return m_rfsRepository;
144    }
145
146    /**
147     * Saves the given file content in the disk cache.<p>
148     *
149     * @param rfsName the RFS name of the file to save the content in
150     * @param content the content of the file to save
151     * @param dateLastModified the date of last modification to set for the save file
152     *
153     * @throws IOException in case of disk access errors
154     */
155    public void saveCacheFile(String rfsName, byte[] content, long dateLastModified) throws IOException {
156
157        dateLastModified = simplifyDateLastModified(dateLastModified);
158        File f = saveFile(rfsName, content);
159        // set last modification date
160        f.setLastModified(dateLastModified);
161    }
162
163    /**
164     * Simplifies the "date last modified" from the OpenCms VFS based milliseconds
165     * to just seconds, to support file systems that don't use milliseconds.<p>
166     *
167     * @param dateLastModified the date to simplify
168     *
169     * @return the simplified date last modified
170     */
171    private long simplifyDateLastModified(long dateLastModified) {
172
173        return dateLastModified / 1000L;
174    }
175}