/*
###############################################################################
# #
# Copyright (C) 2011-2016 OpenMEAP, Inc. #
# Credits to Jonathan Schang & Rob Thacher #
# #
# Released under the LGPLv3 #
# #
# OpenMEAP 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 3 of the License, or #
# (at your option) any later version. #
# #
# OpenMEAP 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. #
# #
# You should have received a copy of the GNU Lesser General Public License #
# along with OpenMEAP. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
*/
package com.openmeap.file;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.commons.transaction.file.FileResourceManager;
import org.apache.commons.transaction.file.ResourceManagerException;
import org.apache.commons.transaction.file.ResourceManagerSystemException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.openmeap.model.ModelManager;
import com.openmeap.model.ModelService;
import com.openmeap.model.dto.GlobalSettings;
import com.openmeap.util.SLF4JLoggerFacade;
import com.openmeap.util.Utils;
/**
* FileOperationManager for transaction safe file operations.
* Essentially just a thin-wrapper over org.apache.commons.transaction.file
*
* @author schang
*/
public class FileOperationManagerImpl implements FileOperationManager {
Logger logger = LoggerFactory.getLogger(FileOperationManagerImpl.class);
ModelService modelService;
FileResourceManager fileResourceManager;
Map<Thread,Object> activeTransactions = Collections.synchronizedMap(new HashMap<Thread,Object>());
public void setFileResourceManager(FileResourceManager fileResourceManager) {
this.fileResourceManager = fileResourceManager;
}
public void setModelService(ModelService modelService) {
this.modelService = modelService;
}
@Override
public void deleteDir(String path) throws FileOperationException {
String storeDir = this.fileResourceManager.getStoreDir();
String workDir = this.fileResourceManager.getWorkDir();
_deleteDir(storeDir,storeDir+File.separator+path);
_deleteDir(workDir,workDir+File.separator+path);
delete(path);
}
@Override
public void delete(String path) throws FileOperationException {
Object txId = activeTransactions.get(Thread.currentThread());
logger.trace("Marking delete for {}",path);
try {
fileResourceManager.deleteResource(txId,path,true);
} catch (ResourceManagerException e) {
throw new FileOperationException(e);
}
}
@Override
public boolean exists(String path) throws FileOperationException {
Object txId = activeTransactions.get(Thread.currentThread());
try {
return fileResourceManager.resourceExists(txId,path) || new File(fileResourceManager.getStoreDir()+File.separator+path).exists();
} catch (ResourceManagerException e) {
throw new FileOperationException(e);
}
}
@Override
public void create(String path) throws FileOperationException {
Object txId = activeTransactions.get(Thread.currentThread());
try {
fileResourceManager.createResource(txId,path,false);
} catch (ResourceManagerException e) {
throw new FileOperationException(e);
}
}
@Override
public void copy(String src, String dest) throws FileOperationException {
Object txId = activeTransactions.get(Thread.currentThread());
try {
fileResourceManager.copyResource(txId,src,dest,false);
} catch (ResourceManagerException e) {
throw new FileOperationException(e);
}
}
@Override
public void move(String src, String dest) throws FileOperationException {
Object txId = activeTransactions.get(Thread.currentThread());
try {
fileResourceManager.moveResource(txId,src,dest,false);
} catch (ResourceManagerException e) {
throw new FileOperationException(e);
}
}
@Override
public InputStream read(String path) throws FileOperationException {
Object txId = activeTransactions.get(Thread.currentThread());
try {
return fileResourceManager.readResource(txId,path);
} catch (ResourceManagerException e) {
throw new FileOperationException(e);
}
}
@Override
public OutputStream write(String path) throws FileOperationException {
Object txId = activeTransactions.get(Thread.currentThread());
try {
return fileResourceManager.writeResource(txId,path);
} catch (ResourceManagerException e) {
throw new FileOperationException(e);
}
}
@Override
public void begin() throws FileOperationException {
_setup();
Object txId;
try {
fileResourceManager.start();
txId = fileResourceManager.generatedUniqueTxId();
fileResourceManager.startTransaction(txId);
} catch (ResourceManagerSystemException e) {
throw new FileOperationException(e);
} catch (ResourceManagerException e) {
throw new FileOperationException(e);
}
activeTransactions.put(Thread.currentThread(), txId);
}
@Override
public void commit() throws FileOperationException {
try {
fileResourceManager.commitTransaction(activeTransactions.get(Thread.currentThread()));
} catch (ResourceManagerException e) {
throw new FileOperationException(e);
}
}
@Override
public void rollback() throws FileOperationException {
try {
fileResourceManager.rollbackTransaction(activeTransactions.get(Thread.currentThread()));
} catch (ResourceManagerException e) {
throw new FileOperationException(e);
}
}
@Override
public void unzipFile(ZipFile zipFile, String destinationDir) throws FileOperationException {
try {
int BUFFER = 1024;
BufferedOutputStream dest = null;
BufferedInputStream is = null;
OutputStream fos = null;
ZipEntry entry;
Enumeration e = zipFile.entries();
while(e.hasMoreElements()) {
try {
entry = (ZipEntry) e.nextElement();
is = new BufferedInputStream(zipFile.getInputStream(entry));
String newFile = destinationDir+File.separator+entry.getName();
if( entry.isDirectory() ) {
continue; // skip directories, resource manager will create for us
} else {
fos = write(newFile);
dest = new BufferedOutputStream(fos, BUFFER);
Utils.pipeInputStreamIntoOutputStream(is, dest);
}
} finally {
if( dest!=null ) {
dest.close();
}
if( fos!=null ) {
fos.close();
}
if( is!=null ) {
is.close();
}
}
}
} catch(IOException ioe) {
throw new FileOperationException(ioe);
}
}
private void _deleteDir(String prefix, String absolutePath) throws FileOperationException {
File absFile = new File(absolutePath);
String absPath = absFile.getAbsolutePath();
String superPrefix = absFile.getAbsolutePath().substring(0,absPath.length()-absolutePath.length()-1);
if(absFile.exists() && absFile.isDirectory()) {
File[] files = absFile.listFiles();
if(files==null) {
return;
}
for(File thisFile : files) {
String fullPath = thisFile.getAbsolutePath();
String relativePath = fullPath.substring(superPrefix.length()+prefix.length()+2);
if(thisFile.isDirectory()) {
_deleteDir(prefix, prefix+File.separator+relativePath);
} else {
delete(relativePath);
}
}
}
}
private void _setup() throws FileOperationException {
if(fileResourceManager!=null) {
return;
}
GlobalSettings settings = (GlobalSettings)modelService.findByPrimaryKey(GlobalSettings.class, 1L);
if( settings.getTemporaryStoragePath()==null
|| !new File(settings.getTemporaryStoragePath()).exists()) {
String msg = "The storage path has not been set in GlobalSettings. Use the settings page to fix this.";
logger.error(msg);
throw new FileOperationException(msg);
}
FileResourceManager resMgr = new FileResourceManager(
settings.getTemporaryStoragePath()
,settings.getTemporaryStoragePath()+"/tmp",
false,
new SLF4JLoggerFacade(LoggerFactory.getLogger(FileResourceManager.class)));
fileResourceManager = resMgr;
}
@Override
public boolean isTransactionActive() throws FileOperationException {
return activeTransactions.get(Thread.currentThread())!=null;
}
}