/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
package divconq.service.simple;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import com.sun.jna.Platform;
import net.contentobjects.jnotify.JNotify;
import net.contentobjects.jnotify.JNotifyException;
import net.contentobjects.jnotify.JNotifyListener;
import divconq.bus.IService;
import divconq.bus.Message;
import divconq.bus.MessageUtil;
import divconq.filestore.CommonPath;
import divconq.filestore.local.FileSystemDriver;
import divconq.filestore.local.FileSystemFile;
import divconq.hub.Hub;
import divconq.lang.op.FuncResult;
import divconq.lang.op.OperationContext;
import divconq.log.Logger;
import divconq.mod.ExtensionBase;
import divconq.struct.CompositeParser;
import divconq.struct.CompositeStruct;
import divconq.struct.RecordStruct;
import divconq.util.StringUtil;
import divconq.work.ScriptWork;
import divconq.work.Task;
import divconq.work.TaskRun;
import divconq.xml.XElement;
public class FileWatcher extends ExtensionBase implements IService {
//protected HashMap<WatchKey, XElement> watches = new HashMap<>();
//protected HashMap<WatchKey, FileSystemDriver> stores = new HashMap<>();
//protected boolean stopped = false;
protected List<Integer> watchids = new ArrayList<>();
@Override
public void start() {
XElement settings = this.getLoader().getSettings();
if (settings != null) {
try {
for (XElement watch : settings.selectAll("Watch")) {
String path = watch.getAttribute("FilePath");
if (StringUtil.isEmpty(path))
continue;
Path dir = Paths.get(path);
Files.createDirectories(dir);
FileSystemDriver drv = new FileSystemDriver(dir);
try {
int watchID = JNotify.addWatch(
path,
JNotify.FILE_CREATED | JNotify.FILE_DELETED | JNotify.FILE_MODIFIED | JNotify.FILE_RENAMED,
true,
new JNotifyListener() {
public void fileRenamed(int wd, String rootPath, String oldName, String newName) {
this.trigger(JNotify.FILE_RENAMED, rootPath, newName, oldName);
}
public void fileModified(int wd, String rootPath, String name) {
this.trigger(JNotify.FILE_MODIFIED, rootPath, name, null);
}
public void fileDeleted(int wd, String rootPath, String name) {
this.trigger(JNotify.FILE_DELETED, rootPath, name, null);
}
public void fileCreated(int wd, String rootPath, String name) {
this.trigger(JNotify.FILE_CREATED, rootPath, name, null);
}
public void trigger(int type, String rootPath, String name, String oldName) {
// ignore mac hidden file
if (Platform.isMac() && name.endsWith(".DS_Store"))
return;
String tasktag = "CreateTask";
if (type == JNotify.FILE_MODIFIED)
tasktag = "ModifyTask";
else if (type == JNotify.FILE_DELETED)
tasktag = "DeleteTask";
else if (type == JNotify.FILE_RENAMED)
tasktag = "RenameTask";
for (XElement task : watch.selectAll(tasktag)) {
String id = task.getAttribute("Id");
if (StringUtil.isEmpty(id))
id = Task.nextTaskId();
String title = task.getAttribute("Title");
String script = task.getAttribute("Script");
String params = task.selectFirstText("Params");
RecordStruct prec = null;
if (StringUtil.isNotEmpty(params)) {
FuncResult<CompositeStruct> pres = CompositeParser.parseJson(params);
if (pres.isNotEmptyResult())
prec = (RecordStruct) pres.getResult();
}
if (prec == null)
prec = new RecordStruct();
FileSystemFile fh = new FileSystemFile(drv, new CommonPath("/" + name));
prec.setField("File", fh);
//prec.setField("FullPath", parent.resolve(fileName).toString());
//prec.setField("RelativePath", fileName.toString());
if (script.startsWith("$"))
script = script.substring(1);
Task t = new Task()
.withId(id)
.withTitle(title)
.withParams(prec)
.withRootContext();
if (!ScriptWork.addScript(t, Paths.get(script))) {
Logger.error("Unable to run script for file watcher: " + watch.getAttribute("FilePath"));
continue;
}
Hub.instance.getWorkPool().submit(t);
}
}
}
);
this.watchids.add(watchID);
}
catch (Exception x) {
OperationContext.get().error("Unable to add file watcher for " + dir + " - error: " + x);
}
}
/*
ISystemWork watchwork = new ISystemWork() {
protected boolean closed = false;
@Override
public void run(SysReporter reporter) {
if (this.closed)
return;
if (FileWatcher.this.stopped) {
try {
watcher.close();
}
catch (IOException x) {
}
this.closed = true;
return;
}
WatchKey key = watcher.poll();
if (key == null)
return;
FileSystemDriver drv = FileWatcher.this.stores.get(key);
XElement watch = FileWatcher.this.watches.get(key);
//Path parent = (Path) key.watchable();
try {
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
Path fileName = (Path) event.context();
System.out.println(kind.name() + ": " + fileName);
String tasktag = "CreateTask";
if (kind == ENTRY_MODIFY)
tasktag = "ModifyTask";
else if (kind == ENTRY_DELETE)
tasktag = "DeleteTask";
for (XElement task : watch.selectAll(tasktag)) {
String id = task.getAttribute("Id");
if (StringUtil.isEmpty(id))
id = Session.nextTaskId();
String title = task.getAttribute("Title");
String script = task.getAttribute("Script");
String params = task.selectFirstText("Params");
RecordStruct prec = null;
if (StringUtil.isNotEmpty(params)) {
FuncResult<CompositeStruct> pres = CompositeParser.parseJson(params);
if (pres.isNotEmptyResult())
prec = (RecordStruct) pres.getResult();
}
if (prec == null)
prec = new RecordStruct();
FileSystemFile fh = new FileSystemFile(drv, new CommonPath("/" + fileName.toString()), false);
prec.setField("File", fh);
//prec.setField("FullPath", parent.resolve(fileName).toString());
//prec.setField("RelativePath", fileName.toString());
if (script.startsWith("$"))
script = script.substring(1);
Task t = new Task()
.withId(id)
.withTitle(title)
.withParams(prec)
.withRootContext();
if (!ScriptWork.addScript(t, Paths.get(script))) {
Logger.error("Unable to run script for file watcher: " + watch.getAttribute("FilePath"));
continue;
}
Hub.instance.getWorkPool().submit(t);
}
}
}
catch (Exception ex) {
System.err.println(ex);
}
finally {
key.reset();
}
}
@Override
public int period() {
return 1;
}
};
Hub.instance.getClock().addSlowSystemWorker(watchwork);
*/
}
catch (IOException ex) {
System.err.println(ex);
}
}
super.start();
}
@Override
public void stop() {
for (int ids : this.watchids)
try {
JNotify.removeWatch(ids);
}
catch (JNotifyException x) {
}
super.stop();
}
@Override
public void handle(TaskRun request) {
Message msg = MessageUtil.message(request);
String feature = msg.getFieldAsString("Feature");
String op = msg.getFieldAsString("Op");
/*
if ("Manager".equals(feature)) {
if ("LoadAll".equals(op)) {
ListStruct names = new ListStruct("root", "localhost");
ExtensionLoader el = this.getLoader();
if (el != null) {
XElement config = el.getSettings();
if (config != null)
for (XElement del : config.selectAll("Domain"))
names.addItem(del.getAttribute("Name"));
}
request.setResult(new ListStruct(
new RecordStruct(
new FieldStruct("Id", "00000_000000000000001"),
new FieldStruct("Title", "root"),
new FieldStruct("Names", names)
)
));
request.complete();
return;
}
}
*/
request.errorTr(441, this.serviceName(), feature, op);
request.complete();
}
}