package com.constellio.data.io.concurrent.filesystem;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.FileUtils;
import com.constellio.data.io.concurrent.data.DataWithVersion;
import com.constellio.data.io.concurrent.exception.AtomicIOException;
import com.constellio.data.io.concurrent.exception.OptimisticLockingException;
import com.constellio.data.io.concurrent.exception.UnsupportedPathException;
import com.constellio.data.utils.hashing.HashingService;
import com.constellio.data.utils.hashing.HashingServiceException;
/**
* A {@link AtomicFileSystem} that works on the local fileSystem
* @author doculibre
*
*/
public class AtomicLocalFileSystem implements AtomicFileSystem {
private HashingService hashingService;
public AtomicLocalFileSystem(HashingService hashingService) {
this.hashingService = hashingService;
}
@Override
public synchronized DataWithVersion readData(String path) {
try {
File aFile = makeFile(path);
byte[] content = null;
String version = null;
if (!aFile.isDirectory()) {
content = FileUtils.readFileToByteArray(aFile);
version = hashingService.getHashFromBytes(content);
}
return new DataWithVersion(content, version);
} catch (FileNotFoundException e) {
throw new com.constellio.data.io.concurrent.exception.FileNotFoundException(e);
} catch (IOException | HashingServiceException e) {
throw new AtomicIOException(e);
}
}
private File makeFile(String path) {
return new File(path);
}
@Override
public synchronized DataWithVersion writeData(String path, DataWithVersion dataWithVersion) {
checkPath(path, dataWithVersion.getVersion());
try {
byte[] bytes = dataWithVersion.getData();
FileUtils.writeByteArrayToFile(makeFile(path), bytes);
String newVersion = hashingService.getHashFromBytes(bytes);
return new DataWithVersion(bytes, newVersion);
} catch (IOException | HashingServiceException e) {
throw new AtomicIOException(e);
}
}
private String checkPath(String path, Object version) {
if (version != null) {
DataWithVersion readData = readData(path);
if (!readData.getVersion().equals(version))
throw new OptimisticLockingException();
}
if (!path.contains(File.separator) || path.startsWith("//"))
throw new UnsupportedPathException(
"All paths should contain " + File.separator + " and should not start with //: " + path);
return path;
}
@Override
public synchronized void delete(String path, Object version) {
path = checkPath(path, version);
File aFile = makeFile(path);
if (aFile.isFile()) {
aFile.delete();
} else {
try {
FileUtils.deleteDirectory(aFile);
} catch (IOException e) {
throw new AtomicIOException(e);
}
}
}
@Override
public synchronized List<String> list(String path) {
List<String> files = new ArrayList<>();
File aDir = makeFile(path);
if (!aDir.exists() || aDir.isFile())
return null;
for (File file : aDir.listFiles()) {
files.add(file.getAbsolutePath());
}
return files;
}
@Override
public synchronized boolean exists(String path) {
return makeFile(path).exists();
}
@Override
public synchronized boolean isDirectory(String path) {
path = checkPath(path, null);
return makeFile(path).isDirectory();
}
@Override
public synchronized boolean mkdirs(String path) {
path = checkPath(path, null);
return makeFile(path).mkdir();
}
@Override
public void close() {
}
}