All Downloads are FREE. Search and download functionalities are using the official Maven repository.

step.grid.filemanager.FileManagerImpl Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (C) 2020, exense GmbH
 *  
 * This file is part of STEP
 *  
 * STEP is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *  
 * STEP 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 Affero General Public License for more details.
 *  
 * You should have received a copy of the GNU Affero General Public License
 * along with STEP.  If not, see .
 ******************************************************************************/
package step.grid.filemanager;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import ch.exense.commons.io.FileHelper;

/**
 * Default implementation of {@link FileManager} which stores registered {@link FileVersion} objects
 * on the file system
 *
 */
public class FileManagerImpl extends AbstractFileManager implements FileManager {

	private static final Logger logger = LoggerFactory.getLogger(FileManagerImpl.class);

	protected ConcurrentHashMap fileIdRegistry = new ConcurrentHashMap<>();
	private LoadingCache fileModificationCache;
	
	public FileManagerImpl(File cacheFolder) {
		this(cacheFolder, new FileManagerImplConfig());
	}
	
	public FileManagerImpl(File cacheFolder, FileManagerImplConfig config) {
		super(cacheFolder, config);
		loadCache();
		
		fileModificationCache = CacheBuilder.newBuilder()
					.concurrencyLevel(config.getFileLastModificationCacheConcurrencyLevel())
					.maximumSize(config.getFileLastModificationCacheMaximumsize())
					.expireAfterWrite(config.getFileLastModificationCacheExpireAfter(), TimeUnit.MILLISECONDS)
					.build(new CacheLoader() {
						public Long load(File file) {
							return FileHelper.getLastModificationDateRecursive(file);
						}
					});
	}

	@Override
	protected void onFileLoad(String registryIndex, String fileId) {
		fileIdRegistry.put(registryIndex, fileId);
		super.onFileLoad(registryIndex, fileId);
	}
	
	@Override
	public FileVersion registerFileVersion(File file, boolean deletePreviousVersions, boolean cleanable) throws FileManagerException {
		String registryIndex = getRegistryIndex(file);
		
		String fileId = fileIdRegistry.computeIfAbsent(registryIndex, f->UUID.randomUUID().toString());
		String version = computeFileVersion(file);
		
		final FileVersionId fileVersionId = new FileVersionId(fileId, version);

		return registerFileVersion(deletePreviousVersions, registryIndex, fileId, fileVersionId, () -> {
			try {
				return storeFile(file, fileVersionId, cleanable);
			} catch (FileManagerException | IOException e) {
				throw new RuntimeException(e);
			}
		});
	}


	@Override
	public FileVersion registerFileVersion(InputStream inputStream, String fileName, boolean isDirectory, boolean deletePreviousVersions, boolean cleanable) throws FileManagerException {
		String registryIndex = fileName;
		
		String fileId = fileIdRegistry.computeIfAbsent(registryIndex, f->UUID.randomUUID().toString());
		String version;

		Path tempFile;
		try {
			tempFile = Files.createTempFile(fileName, "");
			Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING);
			try (FileInputStream is = new FileInputStream(tempFile.toFile())) {
				version = getMD5Checksum(is);
			}
		} catch (IOException e) {
			throw new FileManagerException(null, "Error while getting MD5 checksum for resource "+fileName, e);
		}
		
		final FileVersionId fileVersionId = new FileVersionId(fileId, version);
		FileVersion fileVersion = registerFileVersion(deletePreviousVersions, registryIndex, fileId, fileVersionId, () -> {
			try {
				return storeStream(tempFile.toFile(), fileName, fileVersionId, isDirectory, cleanable);
			} catch (FileManagerException | IOException e) {
				throw new RuntimeException(e);
			}
		});
		try {
			Files.deleteIfExists(tempFile);
		} catch (IOException e) {
			logger.error("Error while deleting temp file "+tempFile);
		}
		return fileVersion;
	}
	

	private FileVersion registerFileVersion(boolean deletePreviousVersions, String filePath, String fileId,
			FileVersionId fileVersionId, Supplier storeFunction) throws FileManagerException {
		if(logger.isDebugEnabled()) {
			logger.debug("Registering file '" + filePath + "' with version "+fileVersionId);
		}
		try {
			fileHandleCacheLock.readLock().lock();
			Map versionCache = getVersionMap(fileId);
			synchronized (versionCache) {
				if (deletePreviousVersions) {
					if (logger.isDebugEnabled()) {
						logger.debug("Removing previous versions for file '" + filePath + "'");
					}
					versionCache.clear();
					FileHelper.deleteFolder(getFileCacheFolder(fileId));
				}

				CachedFileVersion cachedFileVersion = versionCache.get(fileVersionId);
				if (cachedFileVersion == null) {
					try {
						cachedFileVersion = storeFunction.get();
					} catch (Exception e) {
					throw new FileManagerException(fileVersionId, "Error while registering file " + filePath, e);
					}
					versionCache.put(fileVersionId, cachedFileVersion);
					if (logger.isDebugEnabled()) {
						logger.debug("Registered file version '" + cachedFileVersion.getFileVersion() + "'");
					}
				} else {
					cachedFileVersion.updateLastAccessTime();
					if (logger.isDebugEnabled()) {
						logger.debug("File '" + filePath + "' with version " + fileVersionId + " already registered.");
					}
				}
				return cachedFileVersion.getFileVersion();
			}
		} finally {
			fileHandleCacheLock.readLock().unlock();
		}
	}

	protected CachedFileVersion storeFile(File source, FileVersionId fileVersionId, boolean cleanable) throws FileManagerException, IOException {
		File container = getFileVersionCacheFolder(fileVersionId);
		container.mkdirs();
		
		File target;
		boolean isDirectory;
		if(!source.isDirectory()) {
			target = new File(container.getPath()+"/"+source.getName());
			Files.copy(source.toPath(), target.toPath());
			isDirectory = false;
		} else {
			target = new File(container.getPath()+"/"+source.getName()+".zip");
			FileHelper.zip(source, target);
			isDirectory = true;
		}
		
		FileVersion fileVersion = new FileVersion(target, fileVersionId, isDirectory);
		CachedFileVersion cachedFileVersion = new CachedFileVersion(fileVersion, cleanable);
		createMetaFile(source.getAbsolutePath(), cachedFileVersion);
		return cachedFileVersion;
	}
	
	private CachedFileVersion storeStream(File tempFileFromStream, String fileName, FileVersionId fileVersionId, boolean isDirectory, boolean cleanable) throws FileManagerException, IOException {
		File container = getFileVersionCacheFolder(fileVersionId);
		container.mkdirs();
		
		File target;
		if(!isDirectory) {
			target = new File(container.getPath()+"/"+fileName);
		} else {
			target = new File(container.getPath()+"/"+fileName+".zip");
		}
		Files.move(tempFileFromStream.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
		
		FileVersion fileVersion = new FileVersion(target, fileVersionId, isDirectory);
		CachedFileVersion cachedFileVersion = new CachedFileVersion(fileVersion, cleanable);
		createMetaFile(fileName, cachedFileVersion);
		return cachedFileVersion;
	}
	

	private String computeFileVersion(File file) {
		try {
			return Long.toString(fileModificationCache.get(file));
		} catch (ExecutionException e) {
			throw new RuntimeException("Error while getting last modification date for file '"+file.getAbsolutePath()+"' from cache",e);
		}
	}
	
	@Override
	public FileVersion getFileVersion(FileVersionId fileVersionId) throws FileManagerException {
		try {
			fileHandleCacheLock.readLock().lock();
			Map versionCache = getVersionMap(fileVersionId.getFileId());
			synchronized (versionCache) {
				CachedFileVersion cachedFileVersion = versionCache.get(fileVersionId);
				if (cachedFileVersion == null) {
					return null;
				} else {
					cachedFileVersion.updateLastAccessTime();
					return cachedFileVersion.getFileVersion();
				}
			}
		} finally {
			fileHandleCacheLock.readLock().unlock();
		}
	}
	
	@Override
	public void unregisterFileVersion(FileVersionId fileVersionId) {
		removeFileVersion(fileVersionId);
	}



	private String getMD5Checksum(InputStream is) throws IOException {
		return DigestUtils.md5Hex(is);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy