/*
* Copyright 2015 Evgeny Dolganov (evgenij.dolganov@gmail.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package och.util.file;
import static och.util.Util.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import och.util.FileUtil;
import och.util.StreamUtil;
import och.util.Util;
import org.apache.commons.logging.Log;
public abstract class AbstractFileStorage {
private LockManager lockManager = LockManager.instance;
public abstract File getFile(String path) throws Exception;
public String readFileUTF8(String path) throws Exception {
return readFile(path, UTF8);
}
public void writeFileUTF8(String path, boolean overrideIfExists, String content) throws Exception {
writeFile(path, overrideIfExists, content, UTF8);
}
public String readFile(String path, final String charset) throws Exception {
final String[] out = new String[1];
readFile(path, new InputStreamCallback() {
@Override
public void onOpenStream(FileInputStream is) throws Exception {
String content = FileUtil.readFile(is, charset);
out[0] = content;
}
});
return out[0];
}
private void writeFile(String path, boolean overrideIfExists, final String content, final String charset) throws Exception {
writeFile(path, overrideIfExists, new OutputStreamCallback() {
@Override
public void onOpenStream(FileOutputStream os) throws Exception {
FileUtil.writeFile(os, content, charset);
}
});
}
public List<String> getChidrenNames(String path) throws Exception {
lockManager.globalReadLock.lock();
try {
File dir = getFile(path);
if( ! dir.exists() || ! dir.isDirectory()){
return Collections.emptyList();
}
String[] names = dir.list();
return Util.list(names);
}finally{
lockManager.globalReadLock.unlock();
}
}
public void deleteFileOrDir(String path) throws Exception{
lockManager.globalWriteLock.lock();
try {
File file = getFile(path);
FileUtil.deleteFileOrDirRecursive(file);
}finally{
lockManager.globalWriteLock.unlock();
}
}
public void readFile(String path, InputStreamCallback callback) throws Exception {
lockManager.globalReadLock.lock();
try {
Object lock = lockManager.getFileLock(path);
synchronized (lock) {
try {
File file = getFile(path);
if( ! file.exists()){
return;
}
FileInputStream is = new FileInputStream(file);
callback.onOpenStream(is);
StreamUtil.close(is);
}finally {
lockManager.releaseFileLock(path);
}
}
}finally{
lockManager.globalReadLock.unlock();
}
}
public void writeFile(String path, boolean overrideIfExists, OutputStreamCallback callback) throws Exception {
lockManager.globalReadLock.lock();
try {
Object lock = lockManager.getFileLock(path);
synchronized (lock) {
try {
File file = getFile(path);
if( file.exists() && ! overrideIfExists){
return;
}
file.getParentFile().mkdirs();
file.delete();
file.createNewFile();
FileOutputStream os = new FileOutputStream(file);
callback.onOpenStream(os);
StreamUtil.close(os);
}finally {
lockManager.releaseFileLock(path);
}
}
}finally{
lockManager.globalReadLock.unlock();
}
}
public void globalLock(GlobalLockCallback callback) throws Exception {
lockManager.globalWriteLock.lock();
try {
callback.onLock();
}finally{
lockManager.globalWriteLock.unlock();
}
}
}
class LockManager {
private static class FileLockObject {
int clientsCount;
}
static final LockManager instance = new LockManager();
private Log log = getLog(LockManager.class);
private HashMap<String, FileLockObject> locks = new HashMap<String, FileLockObject>();
private ReadWriteLock rw = new ReentrantReadWriteLock();
final Lock globalReadLock = rw.readLock();
final Lock globalWriteLock = rw.writeLock();
private LockManager() {
super();
}
public synchronized Object getFileLock(String path) {
FileLockObject lock = locks.get(path);
if(lock == null){
lock = new FileLockObject();
locks.put(path, lock);
}
lock.clientsCount++;
return lock;
}
public synchronized void releaseFileLock(String path) {
FileLockObject lock = locks.get(path);
if(lock == null){
log.warn("error locks state: can't find lock to release by path '"+path+"'");
return;
}
lock.clientsCount--;
if(lock.clientsCount == 0){
locks.remove(path);
}
}
}