/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.store.file.system;
import com.liferay.document.library.kernel.exception.DuplicateFileException;
import com.liferay.document.library.kernel.exception.NoSuchFileException;
import com.liferay.document.library.kernel.store.BaseStore;
import com.liferay.document.library.kernel.store.Store;
import com.liferay.document.library.kernel.util.DLUtil;
import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil;
import com.liferay.portal.convert.documentlibrary.FileSystemStoreRootDirException;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.FileUtil;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.PropsUtil;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.store.file.system.configuration.FileSystemStoreConfiguration;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Reference;
/**
* @author Brian Wing Shun Chan
* @author Sten Martinez
* @author Alexander Chow
* @author Edward Han
* @author Manuel de la Peña
*/
@Component(
configurationPid = "com.liferay.portal.store.file.system.configuration.FileSystemStoreConfiguration",
configurationPolicy = ConfigurationPolicy.OPTIONAL, immediate = true,
property = "store.type=com.liferay.portal.store.file.system.FileSystemStore",
service = Store.class
)
public class FileSystemStore extends BaseStore {
@Override
public void addDirectory(
long companyId, long repositoryId, String dirName) {
File dirNameDir = getDirNameDir(companyId, repositoryId, dirName);
if (dirNameDir.exists()) {
return;
}
try {
FileUtil.mkdirs(dirNameDir);
}
catch (IOException ioe) {
throw new SystemException(ioe);
}
}
@Override
public void addFile(
long companyId, long repositoryId, String fileName, InputStream is)
throws DuplicateFileException {
try {
File fileNameVersionFile = getFileNameVersionFile(
companyId, repositoryId, fileName, VERSION_DEFAULT);
if (fileNameVersionFile.exists()) {
throw new DuplicateFileException(
companyId, repositoryId, fileName);
}
FileUtil.write(fileNameVersionFile, is);
}
catch (IOException ioe) {
throw new SystemException(ioe);
}
}
@Override
public void checkRoot(long companyId) {
}
@Override
public void copyFileVersion(
long companyId, long repositoryId, String fileName,
String fromVersionLabel, String toVersionLabel)
throws DuplicateFileException, NoSuchFileException {
File fromFileNameVersionFile = getFileNameVersionFile(
companyId, repositoryId, fileName, fromVersionLabel);
if (!fromFileNameVersionFile.exists()) {
throw new NoSuchFileException(
companyId, repositoryId, fileName, fromVersionLabel);
}
File toFileNameVersionFile = getFileNameVersionFile(
companyId, repositoryId, fileName, toVersionLabel);
if (toFileNameVersionFile.exists()) {
throw new DuplicateFileException(toFileNameVersionFile.getPath());
}
try {
toFileNameVersionFile.createNewFile();
FileUtil.copyFile(fromFileNameVersionFile, toFileNameVersionFile);
}
catch (IOException ioe) {
throw new SystemException(ioe);
}
}
@Override
public void deleteDirectory(
long companyId, long repositoryId, String dirName) {
File dirNameDir = getDirNameDir(companyId, repositoryId, dirName);
if (!dirNameDir.exists()) {
logFailedDeletion(companyId, repositoryId, dirName);
return;
}
File parentFile = dirNameDir.getParentFile();
FileUtil.deltree(dirNameDir);
RepositoryDirKey repositoryDirKey = new RepositoryDirKey(
companyId, repositoryId);
_repositoryDirs.remove(repositoryDirKey);
deleteEmptyAncestors(parentFile);
}
@Override
public void deleteFile(long companyId, long repositoryId, String fileName) {
File fileNameDir = getFileNameDir(companyId, repositoryId, fileName);
if (!fileNameDir.exists()) {
logFailedDeletion(companyId, repositoryId, fileName);
return;
}
File parentFile = fileNameDir.getParentFile();
FileUtil.deltree(fileNameDir);
deleteEmptyAncestors(companyId, repositoryId, parentFile);
}
@Override
public void deleteFile(
long companyId, long repositoryId, String fileName,
String versionLabel) {
File fileNameVersionFile = getFileNameVersionFile(
companyId, repositoryId, fileName, versionLabel);
if (!fileNameVersionFile.exists()) {
logFailedDeletion(companyId, repositoryId, fileName, versionLabel);
return;
}
File parentFile = fileNameVersionFile.getParentFile();
fileNameVersionFile.delete();
deleteEmptyAncestors(companyId, repositoryId, parentFile);
}
@Override
public File getFile(
long companyId, long repositoryId, String fileName,
String versionLabel)
throws NoSuchFileException {
if (Validator.isNull(versionLabel)) {
versionLabel = getHeadVersionLabel(
companyId, repositoryId, fileName);
}
File fileNameVersionFile = getFileNameVersionFile(
companyId, repositoryId, fileName, versionLabel);
if (!fileNameVersionFile.exists()) {
throw new NoSuchFileException(
companyId, repositoryId, fileName, versionLabel);
}
return fileNameVersionFile;
}
@Override
public byte[] getFileAsBytes(
long companyId, long repositoryId, String fileName)
throws PortalException {
try {
File file = getFile(companyId, repositoryId, fileName);
return Files.readAllBytes(file.toPath());
}
catch (IOException ioe) {
throw new SystemException(ioe);
}
}
@Override
public byte[] getFileAsBytes(
long companyId, long repositoryId, String fileName,
String versionLabel)
throws PortalException {
try {
File file = getFile(
companyId, repositoryId, fileName, versionLabel);
return Files.readAllBytes(file.toPath());
}
catch (IOException ioe) {
throw new SystemException(ioe);
}
}
@Override
public InputStream getFileAsStream(
long companyId, long repositoryId, String fileName,
String versionLabel)
throws NoSuchFileException {
if (Validator.isNull(versionLabel)) {
versionLabel = getHeadVersionLabel(
companyId, repositoryId, fileName);
}
File fileNameVersionFile = getFileNameVersionFile(
companyId, repositoryId, fileName, versionLabel);
try {
return new FileInputStream(fileNameVersionFile);
}
catch (FileNotFoundException fnfe) {
throw new NoSuchFileException(
companyId, repositoryId, fileName, fnfe);
}
}
@Override
public String[] getFileNames(long companyId, long repositoryId) {
File repositoryDir = getRepositoryDir(companyId, repositoryId);
List<String> fileNames = new ArrayList<>();
String[] dirNames = FileUtil.listDirs(repositoryDir);
for (String dirName : dirNames) {
getFileNames(
fileNames, dirName,
repositoryDir.getPath() + StringPool.SLASH + dirName);
}
return fileNames.toArray(new String[fileNames.size()]);
}
@Override
public String[] getFileNames(
long companyId, long repositoryId, String dirName) {
File dirNameDir = getDirNameDir(companyId, repositoryId, dirName);
if (!dirNameDir.exists()) {
return new String[0];
}
List<String> fileNames = new ArrayList<>();
getFileNames(fileNames, dirName, dirNameDir.getPath());
Collections.sort(fileNames);
return fileNames.toArray(new String[fileNames.size()]);
}
@Override
public long getFileSize(long companyId, long repositoryId, String fileName)
throws NoSuchFileException {
String versionLabel = getHeadVersionLabel(
companyId, repositoryId, fileName);
File fileNameVersionFile = getFileNameVersionFile(
companyId, repositoryId, fileName, versionLabel);
if (!fileNameVersionFile.exists()) {
throw new NoSuchFileException(companyId, repositoryId, fileName);
}
return fileNameVersionFile.length();
}
@Override
public boolean hasDirectory(
long companyId, long repositoryId, String dirName) {
File dirNameDir = getDirNameDir(companyId, repositoryId, dirName);
return dirNameDir.exists();
}
@Override
public boolean hasFile(
long companyId, long repositoryId, String fileName,
String versionLabel) {
File fileNameVersionFile = getFileNameVersionFile(
companyId, repositoryId, fileName, versionLabel);
return fileNameVersionFile.exists();
}
@Override
public void updateFile(
long companyId, long repositoryId, long newRepositoryId,
String fileName)
throws DuplicateFileException, NoSuchFileException {
if (repositoryId == newRepositoryId) {
throw new DuplicateFileException(
companyId, newRepositoryId, fileName);
}
File fileNameDir = getFileNameDir(companyId, repositoryId, fileName);
if (!fileNameDir.exists()) {
throw new NoSuchFileException(companyId, repositoryId, fileName);
}
File newFileNameDir = getFileNameDir(
companyId, newRepositoryId, fileName);
if (newFileNameDir.exists()) {
throw new DuplicateFileException(
companyId, newRepositoryId, fileName);
}
File parentFile = fileNameDir.getParentFile();
boolean renamed = FileUtil.move(fileNameDir, newFileNameDir);
if (!renamed) {
throw new SystemException(
"File name directory was not renamed from " +
fileNameDir.getPath() + " to " + newFileNameDir.getPath());
}
deleteEmptyAncestors(companyId, repositoryId, parentFile);
}
@Override
public void updateFile(
long companyId, long repositoryId, String fileName,
String newFileName)
throws DuplicateFileException, NoSuchFileException {
if (fileName.equals(newFileName)) {
throw new DuplicateFileException(
companyId, repositoryId, newFileName);
}
File fileNameDir = getFileNameDir(companyId, repositoryId, fileName);
if (!fileNameDir.exists()) {
throw new NoSuchFileException(companyId, repositoryId, fileName);
}
File newFileNameDir = getFileNameDir(
companyId, repositoryId, newFileName);
if (newFileNameDir.exists()) {
throw new DuplicateFileException(
companyId, repositoryId, newFileName);
}
File parentFile = fileNameDir.getParentFile();
boolean renamed = FileUtil.move(fileNameDir, newFileNameDir);
if (!renamed) {
throw new SystemException(
"File name directory was not renamed from " +
fileNameDir.getPath() + " to " + newFileNameDir.getPath());
}
deleteEmptyAncestors(companyId, repositoryId, parentFile);
}
@Override
public void updateFile(
long companyId, long repositoryId, String fileName,
String versionLabel, InputStream is)
throws DuplicateFileException {
try {
File fileNameVersionFile = getFileNameVersionFile(
companyId, repositoryId, fileName, versionLabel);
if (fileNameVersionFile.exists()) {
throw new DuplicateFileException(
companyId, repositoryId, fileName, versionLabel);
}
FileUtil.write(fileNameVersionFile, is);
}
catch (IOException ioe) {
throw new SystemException(ioe);
}
}
@Override
public void updateFileVersion(
long companyId, long repositoryId, String fileName,
String fromVersionLabel, String toVersionLabel)
throws DuplicateFileException, NoSuchFileException {
File fromFileNameVersionFile = getFileNameVersionFile(
companyId, repositoryId, fileName, fromVersionLabel);
if (!fromFileNameVersionFile.exists()) {
throw new NoSuchFileException(
companyId, repositoryId, fileName, fromVersionLabel);
}
File toFileNameVersionFile = getFileNameVersionFile(
companyId, repositoryId, fileName, toVersionLabel);
if (toFileNameVersionFile.exists()) {
throw new DuplicateFileException(
companyId, repositoryId, fileName, toVersionLabel);
}
boolean renamed = FileUtil.move(
fromFileNameVersionFile, toFileNameVersionFile);
if (!renamed) {
throw new SystemException(
"File name version file was not renamed from " +
fromFileNameVersionFile.getPath() + " to " +
toFileNameVersionFile.getPath());
}
}
@Activate
protected void activate(Map<String, Object> properties) {
_fileSystemStoreConfiguration = ConfigurableUtil.createConfigurable(
FileSystemStoreConfiguration.class, properties);
if (Validator.isBlank(_fileSystemStoreConfiguration.rootDir())) {
throw new IllegalArgumentException(
"File system root directory is not set",
new FileSystemStoreRootDirException());
}
initializeRootDir();
}
protected void deleteEmptyAncestors(File file) {
deleteEmptyAncestors(-1, -1, file);
}
protected void deleteEmptyAncestors(
long companyId, long repositoryId, File file) {
while (file != null) {
if (!file.delete()) {
return;
}
String fileName = file.getName();
if ((repositoryId > 0) &&
fileName.equals(String.valueOf(repositoryId))) {
RepositoryDirKey repositoryDirKey = new RepositoryDirKey(
companyId, repositoryId);
_repositoryDirs.remove(repositoryDirKey);
}
file = file.getParentFile();
}
}
protected File getCompanyDir(long companyId) {
File companyDir = new File(_rootDir + StringPool.SLASH + companyId);
try {
FileUtil.mkdirs(companyDir);
}
catch (IOException ioe) {
throw new SystemException(ioe);
}
return companyDir;
}
protected File getDirNameDir(
long companyId, long repositoryId, String dirName) {
return getFileNameDir(companyId, repositoryId, dirName);
}
protected File getFileNameDir(
long companyId, long repositoryId, String fileName) {
File repositoryDir = getRepositoryDir(companyId, repositoryId);
File fileNameDir = new File(
repositoryDir + StringPool.SLASH + fileName);
return fileNameDir;
}
protected void getFileNames(
List<String> fileNames, String dirName, String path) {
String[] pathDirNames = FileUtil.listDirs(path);
if (ArrayUtil.isNotEmpty(pathDirNames)) {
for (String pathDirName : pathDirNames) {
String subdirName = null;
if (Validator.isBlank(dirName)) {
subdirName = pathDirName;
}
else {
subdirName = dirName + StringPool.SLASH + pathDirName;
}
getFileNames(
fileNames, subdirName,
path + StringPool.SLASH + pathDirName);
}
}
else if (new File(path).isDirectory()) {
fileNames.add(dirName);
}
}
protected File getFileNameVersionFile(
long companyId, long repositoryId, String fileName, String version) {
File fileNameDir = getFileNameDir(companyId, repositoryId, fileName);
File fileNameVersionFile = new File(
fileNameDir + StringPool.SLASH + version);
return fileNameVersionFile;
}
protected String getHeadVersionLabel(
long companyId, long repositoryId, String fileName) {
File fileNameDir = getFileNameDir(companyId, repositoryId, fileName);
if (!fileNameDir.exists()) {
return VERSION_DEFAULT;
}
String[] versionLabels = FileUtil.listFiles(fileNameDir);
String headVersionLabel = VERSION_DEFAULT;
for (String versionLabel : versionLabels) {
if (DLUtil.compareVersions(versionLabel, headVersionLabel) > 0) {
headVersionLabel = versionLabel;
}
}
return headVersionLabel;
}
protected File getRepositoryDir(long companyId, long repositoryId) {
RepositoryDirKey repositoryDirKey = new RepositoryDirKey(
companyId, repositoryId);
File repositoryDir = _repositoryDirs.get(repositoryDirKey);
if (repositoryDir == null) {
File companyDir = getCompanyDir(companyId);
repositoryDir = new File(
companyDir + StringPool.SLASH + repositoryId);
try {
FileUtil.mkdirs(repositoryDir);
}
catch (IOException ioe) {
throw new SystemException(ioe);
}
_repositoryDirs.put(repositoryDirKey, repositoryDir);
}
return repositoryDir;
}
protected String getRootDirName() {
return _fileSystemStoreConfiguration.rootDir();
}
protected void initializeRootDir() {
String path = getRootDirName();
_rootDir = new File(path);
if (!_rootDir.isAbsolute()) {
_rootDir = new File(PropsUtil.get(PropsKeys.LIFERAY_HOME), path);
}
try {
FileUtil.mkdirs(_rootDir);
}
catch (IOException ioe) {
throw new SystemException(ioe);
}
}
@Reference(unbind = "-")
protected void setConfigurationAdmin(
ConfigurationAdmin configurationAdmin) {
this.configurationAdmin = configurationAdmin;
}
protected ConfigurationAdmin configurationAdmin;
private static volatile FileSystemStoreConfiguration
_fileSystemStoreConfiguration;
private final Map<RepositoryDirKey, File> _repositoryDirs =
new ConcurrentHashMap<>();
private File _rootDir;
private static class RepositoryDirKey {
public RepositoryDirKey(long companyId, long repositoryId) {
_companyId = companyId;
_repositoryId = repositoryId;
}
@Override
public boolean equals(Object obj) {
RepositoryDirKey repositoryDirKey = (RepositoryDirKey)obj;
if ((_companyId == repositoryDirKey._companyId) &&
(_repositoryId == repositoryDirKey._repositoryId)) {
return true;
}
else {
return false;
}
}
@Override
public int hashCode() {
return (int)(_companyId * 11 + _repositoryId);
}
private long _companyId;
private long _repositoryId;
}
}