2017-06-16 11:12:58 +02:00
|
|
|
package org.gcube.data.analysis.wps.repository;
|
|
|
|
|
|
|
|
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
|
|
|
|
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.lang.reflect.Method;
|
|
|
|
import java.net.URL;
|
|
|
|
import java.net.URLClassLoader;
|
2017-09-19 17:19:03 +02:00
|
|
|
import java.nio.file.DirectoryIteratorException;
|
|
|
|
import java.nio.file.DirectoryStream;
|
2017-06-16 11:12:58 +02:00
|
|
|
import java.nio.file.FileSystems;
|
2017-09-19 17:19:03 +02:00
|
|
|
import java.nio.file.Files;
|
2017-06-16 11:12:58 +02:00
|
|
|
import java.nio.file.Path;
|
|
|
|
import java.nio.file.Paths;
|
|
|
|
import java.nio.file.WatchEvent;
|
|
|
|
import java.nio.file.WatchKey;
|
|
|
|
import java.nio.file.WatchService;
|
2017-09-19 17:19:03 +02:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
2017-06-16 11:12:58 +02:00
|
|
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
|
|
public final class AlgorithmUpdater {
|
|
|
|
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(AlgorithmUpdater.class);
|
2017-09-19 17:19:03 +02:00
|
|
|
|
2017-06-16 11:12:58 +02:00
|
|
|
private String algorithmDirectory;
|
2017-09-19 17:19:03 +02:00
|
|
|
|
2017-06-16 11:12:58 +02:00
|
|
|
private boolean mustUpdate = false;
|
2017-09-19 17:19:03 +02:00
|
|
|
|
2017-06-16 11:12:58 +02:00
|
|
|
private WatcherThread watcherThread = null;
|
2017-09-19 17:19:03 +02:00
|
|
|
|
2017-06-16 11:12:58 +02:00
|
|
|
public AlgorithmUpdater(String algorithmDirectory) {
|
|
|
|
super();
|
|
|
|
this.algorithmDirectory = algorithmDirectory;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected synchronized boolean mustUpdate(){
|
|
|
|
return mustUpdate;
|
|
|
|
}
|
2017-09-19 17:19:03 +02:00
|
|
|
|
2017-06-16 11:12:58 +02:00
|
|
|
protected synchronized void reset(){
|
|
|
|
mustUpdate = false;
|
|
|
|
}
|
2017-09-19 17:19:03 +02:00
|
|
|
|
2017-06-16 11:12:58 +02:00
|
|
|
public boolean isStarted(){
|
|
|
|
return watcherThread!=null;
|
|
|
|
}
|
2017-09-19 17:19:03 +02:00
|
|
|
|
|
|
|
protected void init(){
|
2017-06-16 11:12:58 +02:00
|
|
|
watcherThread = new WatcherThread(Thread.currentThread().getContextClassLoader(), algorithmDirectory);
|
2017-09-19 17:19:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected void startWhatcher(){
|
2017-06-16 11:12:58 +02:00
|
|
|
watcherThread.start();
|
|
|
|
}
|
2017-09-19 17:19:03 +02:00
|
|
|
|
|
|
|
|
|
|
|
public ClassLoader getLoader() {
|
|
|
|
return this.watcherThread.getLoader();
|
2017-07-17 14:55:08 +02:00
|
|
|
}
|
2017-09-19 17:19:03 +02:00
|
|
|
|
|
|
|
|
2017-06-16 11:12:58 +02:00
|
|
|
private class WatcherThread extends Thread {
|
|
|
|
|
2017-09-19 17:19:03 +02:00
|
|
|
/*private Map<String, Long> justCreated = new WeakHashMap<String, Long>();
|
|
|
|
private static final long ENTRY_MAX_TIME = 300000;*/
|
2017-06-16 11:12:58 +02:00
|
|
|
private WatchService watcher;
|
|
|
|
private ClassLoader loader;
|
2017-09-19 17:19:03 +02:00
|
|
|
private ClassLoader parentLoader;
|
2017-06-16 11:12:58 +02:00
|
|
|
private Path dir;
|
2017-09-19 17:19:03 +02:00
|
|
|
private List<String> installedURLS;
|
|
|
|
|
|
|
|
public WatcherThread(ClassLoader parentLoader, String algorithmDirectory) {
|
2017-06-16 11:12:58 +02:00
|
|
|
super();
|
|
|
|
try {
|
2017-09-19 17:19:03 +02:00
|
|
|
log.debug("Watcher Thread created");
|
2017-06-16 11:12:58 +02:00
|
|
|
watcher = FileSystems.getDefault().newWatchService();
|
2017-09-19 17:19:03 +02:00
|
|
|
this.parentLoader = parentLoader;
|
|
|
|
log.debug("parent class loader is {}", parentLoader.getClass().getSimpleName());
|
2017-06-16 11:12:58 +02:00
|
|
|
dir = Paths.get(algorithmDirectory);
|
2017-09-19 17:19:03 +02:00
|
|
|
installedURLS = updateClassLoader();
|
|
|
|
dir.register(watcher, ENTRY_CREATE);
|
2017-06-16 11:12:58 +02:00
|
|
|
} catch (IOException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-19 17:19:03 +02:00
|
|
|
|
|
|
|
protected ClassLoader getLoader() {
|
|
|
|
return loader;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-06-16 11:12:58 +02:00
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
public void run(){
|
|
|
|
for (;;) {
|
2017-09-18 11:56:38 +02:00
|
|
|
log.info("direcotry watcher is running");
|
2017-06-16 11:12:58 +02:00
|
|
|
// wait for key to be signaled
|
|
|
|
WatchKey key;
|
|
|
|
try {
|
|
|
|
key = watcher.take();
|
|
|
|
} catch (InterruptedException x) {
|
2017-07-17 14:55:08 +02:00
|
|
|
log.error("DIRECTORY WATCHER IS INTERRUPTED",x);
|
2017-06-16 11:12:58 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (WatchEvent<?> event: key.pollEvents()) {
|
|
|
|
WatchEvent.Kind<?> kind = event.kind();
|
2017-09-19 17:19:03 +02:00
|
|
|
|
2017-06-16 11:12:58 +02:00
|
|
|
// This key is registered only
|
|
|
|
// for ENTRY_CREATE events,
|
|
|
|
// but an OVERFLOW event can
|
|
|
|
// occur regardless if events
|
|
|
|
// are lost or discarded.
|
|
|
|
if (kind == OVERFLOW) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The filename is the
|
|
|
|
// context of the event.
|
|
|
|
WatchEvent<Path> ev = (WatchEvent<Path>)event;
|
|
|
|
Path filename = ev.context();
|
2017-09-19 17:19:03 +02:00
|
|
|
|
|
|
|
log.trace("new event thrown for directory watcher with filename {} and kind {}", filename, kind);
|
|
|
|
|
|
|
|
if (filename.toString().endsWith("_interface.jar") ){
|
2017-06-16 11:12:58 +02:00
|
|
|
try{
|
2017-09-19 17:19:03 +02:00
|
|
|
if (installedURLS.contains(filename.getFileName().toString())){
|
|
|
|
log.debug("modifying an already installed algorithm");
|
|
|
|
installedURLS = updateClassLoader();
|
|
|
|
} else {
|
|
|
|
log.debug("installing new algorithm");
|
|
|
|
final Class<URLClassLoader> sysclass = URLClassLoader.class;
|
|
|
|
// TODO some kind of a hack. Need to invent better solution.
|
|
|
|
final Method method = sysclass.getDeclaredMethod("addURL", new Class[] { URL.class });
|
|
|
|
method.setAccessible(true);
|
|
|
|
URL realjarURL = null;
|
|
|
|
URL[] jarURLS ;
|
|
|
|
try{
|
|
|
|
realjarURL = dir.resolve(filename.toString().replaceFirst("_interface", "")).getFileName().toUri().toURL();
|
|
|
|
jarURLS = new URL[] {realjarURL,
|
|
|
|
dir.resolve(filename).toFile().toURI().toURL() };
|
|
|
|
log.debug("found {} ",realjarURL);
|
|
|
|
}catch(Exception ipe){
|
|
|
|
jarURLS = new URL[] {dir.resolve(filename).toFile().toURI().toURL() };
|
|
|
|
log.warn("only {} have been found",filename);
|
|
|
|
}
|
|
|
|
method.invoke(loader, jarURLS );
|
|
|
|
log.info("filename added in loader {}",filename, loader.getClass().getName());
|
|
|
|
installedURLS.add(filename.getFileName().toString());
|
|
|
|
}
|
2017-06-16 11:12:58 +02:00
|
|
|
mustUpdate = true;
|
|
|
|
}catch(Exception e){
|
|
|
|
log.error("filename {} cannot be added to classpath",e,filename);
|
|
|
|
}
|
2017-09-19 17:19:03 +02:00
|
|
|
|
|
|
|
} else log.info("filename {} is not an algorithm interface",filename);
|
2017-06-16 11:12:58 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean valid = key.reset();
|
|
|
|
if (!valid) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-09-19 17:19:03 +02:00
|
|
|
|
|
|
|
private List<String> updateClassLoader(){
|
|
|
|
log.debug("getting the stream from directoy {}",dir.getFileName());
|
|
|
|
List<URL> urls = new ArrayList<URL>();
|
|
|
|
List<String> toReturn = new ArrayList<String>(urls.size());
|
|
|
|
|
|
|
|
DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() {
|
|
|
|
public boolean accept(Path file) throws IOException {
|
|
|
|
return (file.getFileName().toString().endsWith(".jar"));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter )){
|
|
|
|
for (Path file: stream) {
|
|
|
|
log.debug("loading url {}",file.getFileName());
|
|
|
|
urls.add(file.toUri().toURL());
|
|
|
|
toReturn.add(file.getFileName().toString());
|
|
|
|
}
|
|
|
|
} catch (IOException | DirectoryIteratorException x) {
|
|
|
|
log.error("error reading config dir",x);
|
|
|
|
}
|
|
|
|
this.loader = new URLClassLoader(urls.toArray(new URL[urls.size()]), parentLoader);
|
|
|
|
log.debug("loader object is {}", loader);
|
|
|
|
return toReturn;
|
|
|
|
}
|
2017-06-16 11:12:58 +02:00
|
|
|
}
|
2017-09-19 17:19:03 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2017-06-16 11:12:58 +02:00
|
|
|
protected void shutdown(){
|
|
|
|
if (isStarted()){
|
|
|
|
//TODO : kill the watcherThread
|
|
|
|
watcherThread = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|