package com.growcontrol.common.scripting;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import com.poixson.commonjava.Utils.utils;
import com.poixson.commonjava.Utils.xStartable;
import com.poixson.commonjava.Utils.exceptions.RequiredArgumentException;
import com.poixson.commonjava.Utils.threads.xThreadPool;
import com.poixson.commonjava.xLogger.xLog;
public class gcScript implements xStartable {
public final String name;
public final int id;
protected volatile ScriptEngine engine = null;
protected final Object engineLock = new Object();
protected final AtomicBoolean running = new AtomicBoolean(false);
protected final AtomicBoolean processing = new AtomicBoolean(false);
protected final ConcurrentLinkedQueue<ScriptCode> queue = new ConcurrentLinkedQueue<ScriptCode>();
public gcScript() {
this(null);
}
public gcScript(final String name) {
final gcScriptManager manager = gcScriptManager.get();
this.id = manager.getNextId();
this.name = (utils.isEmpty(name) ? "script-"+Integer.toString(this.id) : name);
this.queue.add(
new ScriptCode_Text(
"function log() {"+
" return com.poixson.commonjava.xLogger.xLog.getRoot().get('script').get(ScriptName);"+
"}"+
"function print(text) {"+
" log().info(text);"+
"}"+
"log().info('Starting script..')"
)
);
}
protected void init() {
if(this.engine != null) return;
synchronized(this.engineLock){
if(this.engine != null) return;
final gcScriptManager manager = gcScriptManager.get();
this.engine = manager.getEngine();
if(this.engine == null) {
this.log().severe("Failed to load "+gcScriptManager.LANGUAGE+" scripting engine!");
throw new RuntimeException("Failed to load scripting engine!");
}
this.engine.put("ScriptName", this.name);
}
}
protected static interface ScriptCode {
public void eval(final ScriptEngine engine) throws ScriptException;
}
protected static class ScriptCode_File implements ScriptCode {
protected final File file;
public ScriptCode_File(final File file) throws FileNotFoundException {
if(file == null) throw new RequiredArgumentException("file");
if(!file.isFile()) throw new FileNotFoundException("Script file not found: "+file.toString());
this.file = file;
}
@Override
public void eval(final ScriptEngine engine) throws ScriptException {
if(engine == null) throw new RequiredArgumentException("engine");
try {
engine.eval(new FileReader(this.file));
} catch (FileNotFoundException e) {
throw new ScriptException(e);
}
}
}
protected static class ScriptCode_Text implements ScriptCode {
protected final String text;
public ScriptCode_Text(final String text) {
if(utils.isEmpty(text)) throw new RequiredArgumentException("text");
this.text = text;
}
@Override
public void eval(final ScriptEngine engine) throws ScriptException {
if(engine == null) throw new RequiredArgumentException("engine");
engine.eval(this.text);
}
}
// queue a file to be run
public void queue(final File file) throws FileNotFoundException {
if(file == null) throw new RequiredArgumentException("file");
if(!file.isFile()) throw new FileNotFoundException("Script file not found: "+file.toString());
this.queue.add(
new ScriptCode_File(file)
);
this.queue();
}
// queue code to be run
public void queue(final String code) {
this.queue.add(
new ScriptCode_Text(code)
);
}
protected void queue() {
if(!this.running.get()) return;
if(this.processing.get()) return;
this.getThreadPool()
.runLater(this);
}
@Override
public void Start() {
// only start once
if(!this.running.compareAndSet(false, true))
return;
this.init();
this.queue();
}
@Override
public void Stop() {
this.running.set(false);
}
@Override
public boolean isRunning() {
return this.running.get();
}
public boolean isProcessing() {
return this.processing.get();
}
@Override
public void run() {
if(!this.running.get()) return;
if(!this.processing.compareAndSet(false, true)) return;
// eval queued code
final Iterator<ScriptCode> it = this.queue.iterator();
while(it.hasNext()) {
if(!this.running.get()) break;
final ScriptCode code = it.next();
try {
// eval file
if(code instanceof ScriptCode_File) {
final File file = ((ScriptCode_File) code).file;
try {
this.engine.eval(
new FileReader(file)
);
} catch (FileNotFoundException e) {
this.log().trace(e);
}
} else
// eval text
if(code instanceof ScriptCode_Text) {
this.engine.eval(
((ScriptCode_Text) code).text
);
} else {
this.log().severe("Invalid queued code type: "+code.getClass().getName());
}
} catch (ScriptException e) {
this.log().trace(e);
}
it.remove();
// lets not hold up the thread pool
break;
}
this.processing.set(false);
// just in case
if(!this.queue.isEmpty())
this.queue();
}
// public Object InvokeFunction(final String name) {
// if(!this.running.get()) throw new IllegalStateException("Script not running");
// try {
// return ((Invocable) this.script).invokeFunction(name);
// } catch (NoSuchMethodException e) {
// this.log().trace(e);
// } catch (ScriptException e) {
// this.log().trace(e);
// }
// return null;
// }
public xThreadPool getThreadPool() {
return gcScriptManager.get().getThreadPool();
}
public String getName() {
final String name = this.name;
if(utils.isEmpty(name))
return "script-"+Integer.toString(this.id);
return name;
}
// logger
public xLog log() {
return gcScriptManager.get()
.log().get(this.getName());
}
}