/**
* This file is part of ObjectFabric (http://objectfabric.org).
*
* ObjectFabric is licensed under the Apache License, Version 2.0, the terms
* of which may be found at http://www.apache.org/licenses/LICENSE-2.0.html.
*
* Copyright ObjectFabric Inc.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.objectfabric;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
final class FileSystemView extends ArrayView {
private final File _folder;
private final FileSystemQueue _queue;
FileSystemView(Location location, File folder, FileSystemQueue queue) {
super(location);
_folder = folder;
_queue = queue;
}
final File folder() {
return _folder;
}
@Override
final void getKnown(URI uri) {
long[] ticks = copy();
if (ticks != null) {
if (ticks.length != 0 || !location().isCache())
uri.onKnown(this, ticks);
} else
list(uri, null);
}
@Override
final void onKnown(URI uri, long[] ticks) {
boolean load;
synchronized (this) {
load = isNull();
}
if (load)
list(uri, ticks);
else
getUnknown(uri, ticks);
}
private final void list(final URI uri, final long[] compare) {
ThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
if (Debug.PERSISTENCE_LOG)
Log.write("Folder list " + _folder.getPath());
if (Stats.ENABLED)
Stats.Instance.BlockListCount.incrementAndGet();
String[] files;
try {
files = _folder.list();
} catch (Exception ex) {
Log.write(ex);
files = null;
}
long[] ticks = null;
if (files != null && files.length > 0) {
for (int i = 0; i < files.length; i++)
ticks = Tick.add(ticks, Utils.getTick(files[i]));
} else
ticks = Tick.EMPTY;
onLoad(uri, ticks, compare);
}
});
}
@Override
void getBlock(final URI uri, final long tick) {
if (!contains(tick))
return;
ThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
if (InFlight.starting(uri, tick)) {
File file = new File(_folder, Utils.getTickHex(tick));
if (Debug.PERSISTENCE_LOG)
Log.write("File read " + file.getPath());
List<JVMBuff> list = new List<JVMBuff>();
RandomAccessFile raf = null;
JVMBuff[] buffs = null;
Exception ex = null;
try {
raf = new RandomAccessFile(file, "r");
FileChannel channel = raf.getChannel();
// If file exists and a write is ongoing, wait
while (_queue.ongoing().containsKey(file.getPath())) {
int todo;
Log.write("Waiting on file read!");
Platform.get().sleep(1);
}
// TODO lock file for multi-process?
// if (channel.tryLock(0, Long.MAX_VALUE, true) != null) {
JVMBuff buff = JVMBuff.getWithPosition(0);
int position = buff.position();
int offset = 0;
for (;;) {
int read = channel.read(buff.getByteBuffer(), offset);
if (read < 0) {
if (buff.position() > position) {
buff.limit(buff.position());
buff.position(position);
buff.mark();
list.add(buff);
} else {
if (Debug.ENABLED)
buff.lock(0);
buff.recycle();
}
break;
} else if (buff.remaining() == 0) {
buff.position(position);
buff.mark();
list.add(buff);
buff = JVMBuff.getWithPosition(Buff.getLargestUnsplitable());
position = buff.position();
}
offset += read;
}
buffs = new JVMBuff[list.size()];
list.copyToFixed(buffs);
if (Debug.ENABLED)
for (int i = 0; i < buffs.length; i++)
buffs[i].lock(buffs[i].limit());
// }
} catch (Exception e) {
ex = e;
} finally {
try {
// Closes channel & lock
if (raf != null)
raf.close();
} catch (IOException _) {
// Ignore
}
}
if (Stats.ENABLED)
Stats.Instance.BlockReadCount.incrementAndGet();
if (buffs != null) {
if (Debug.RANDOMIZE_FILE_LOAD_ORDER)
Platform.get().sleep(Platform.get().randomInt(100));
if (Debug.THREADS)
for (int i = 0; i < buffs.length; i++)
ThreadAssert.exchangeGive(buffs, buffs[i]);
if (buffs.length > 0) {
Exception exception = uri.onBlock(FileSystemView.this, tick, buffs, null, true, null, false, null);
if (Debug.THREADS)
ThreadAssert.exchangeTake(buffs);
if (exception != null) {
// TODO make sure exception is related to parsing
Log.write("Corrupted file " + file + ": " + exception.toString());
// TODO Make option or callback to clean corrupted
// file.delete();
}
}
}
if (ex != null && !(ex instanceof FileNotFoundException))
Log.write(ex);
for (int i = 0; i < list.size(); i++)
list.get(i).recycle();
}
}
});
}
@Override
final void onBlock(URI uri, long tick, Buff[] buffs, long[] removals, boolean requested) {
_queue.enqueueBlock(uri, tick, buffs, removals, requested);
}
}