Does the Rhino engine have an api that can stop the execution of a
script fie in the middle. For example, I have a script file in which
there is an infinte loop. How can I stop the execution in the middle?
Of course, I can stop the jvm which started the Rhino engine to
excecute the script. But I don't want to kill the entire jvm session for that reason as i have started the script programatically and the Rhino Engine also running in the same JVM as my application.
Stopping the execution of running JavaScript can be done by using following way.
1) Create a dummy Debugger and attach it to the context created initially.
mContext = Context.enter();
ObservingDebugger observingDebugger = new ObservingDebugger();
mContext.setDebugger(observingDebugger, new Integer(0));
mContext.setGeneratingDebug(true);
mContext.setOptimizationLevel(-1);
The ObservingDebugger code looks as follows.
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.debug.DebugFrame;
import org.mozilla.javascript.debug.DebuggableScript;
import org.mozilla.javascript.debug.Debugger;
public class ObservingDebugger implements Debugger
{
boolean isDisconnected = false;
private DebugFrame debugFrame = null;
public boolean isDisconnected() {
return isDisconnected;
}
public void setDisconnected(boolean isDisconnected) {
this.isDisconnected = isDisconnected;
if(debugFrame != null){
((ObservingDebugFrame)debugFrame).setDisconnected(isDisconnected);
}
}
public ObservingDebugger() {
}
public DebugFrame getFrame(Context cx, DebuggableScript fnOrScript)
{
if(debugFrame == null){
debugFrame = new ObservingDebugFrame(isDisconnected);
}
return debugFrame;
}
#Override
public void handleCompilationDone(Context arg0, DebuggableScript arg1, String arg2) { } }
// internal ObservingDebugFrame class
class ObservingDebugFrame implements DebugFrame
{
boolean isDisconnected = false;
public boolean isDisconnected() {
return isDisconnected;
}
public void setDisconnected(boolean isDisconnected) {
this.isDisconnected = isDisconnected;
}
ObservingDebugFrame(boolean isDisconnected)
{
this.isDisconnected = isDisconnected;
}
public void onEnter(Context cx, Scriptable activation,
Scriptable thisObj, Object[] args)
{ }
public void onLineChange(Context cx, int lineNumber)
{
if(isDisconnected){
throw new RuntimeException("Script Execution terminaed");
}
}
public void onExceptionThrown(Context cx, Throwable ex)
{ }
public void onExit(Context cx, boolean byThrow,
Object resultOrException)
{ }
#Override
public void onDebuggerStatement(Context arg0) { } }
ObservingDebugger class will manage the boolean variable "isDisconnected" and when user clicks on the stop button (wants to stop the execution) then this variable is set to true. Once the variable is set to true as follows the Rhino Execution will immediately terminates.
observingDebugger.setDisconnected(true);
For anyone else looking for a solution, the javadoc for ContextFactory details how to stop a script running more than 10 seconds:
https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/ContextFactory.java
I execute scripts in a new thread using an ExecutorService and timed-out future.get
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(threadEvaluation);
try {
System.out.println("Started..");
future.get(100, TimeUnit.MILLISECONDS);
System.out.println("Finished!");
} catch (TimeoutException e) {
future.cancel(true);
System.out.println("Terminated!");
}
Notice that this approach will not stop the thread executing the script! In order to do so, as the thread executing your script will be notified to be interrupted, you can create a custom ContextFactory that monitors periodically this situation:
public class InterruptableContextFactory extends ContextFactory {
public static boolean initialized = false;
public static void init() {
if (!initialized) {
ContextFactory.initGlobal(new InterruptableContextFactory());
initialized = true;
}
}
#Override
protected void observeInstructionCount(Context cx, int instructionCount) {
System.out.println(instructionCount + " javascript instructions!");
if (Thread.currentThread().isInterrupted()) {
throw new Error("script execution aborted");
}
}
#Override
protected Context makeContext() {
Context cx = super.makeContext();
//set a number of instructions here
cx.setInstructionObserverThreshold(10000);
return cx;
}
}
Before creating any Context object, you need to configure your application to use this ContextFactory as default, just invoke
InterruptableContextFactory.init()
Inside your Callable's call method, you can capture the Error:
try {
cx.setOptimizationLevel(9);
cx.setInstructionObserverThreshold(10000);
ScriptableObject scope = cx.initStandardObjects();
// your code here
} catch (Error e) {
System.out.println("execution was aborted: " + e.getMessage());
} finally {
Context.exit();
}
The Rhino engine doesn't appear to have any mechanism for doing this (bad Rhino!) and it's hard to tell whether it creates threads internally, so the only solution you've got is to create a ThreadGroup, load and execute the Rhino engine and its script from inside a thread in that group, and when you want to kill it off, use ThreadGroup.stop(). Yes, it's deprecated but there's really no other way to do it given that there's no cooperation on the part of the Rhino library.
Related
AIM : To make a generic Thread class that is independent of the parent calling it, can be started/stopped/paused/resumed by the parent class calling it and perform user defined tasks (via runnable)
MY RESEARCH :
SO_1
SO_2
SO_3
SO_4
SO_5
SomeBlog
SomeBlog2
OracleBlog
Problem : from what i have understood:
Starting a background thread: threadObj.start() will execute statements of run() function of a class implementing Runnable Interface.
Stopping a background thread : threadObj.interrupt() will stop a thread from executing
Pausing a thread : threadObj.wait() will pause the thread,although, it requires additional synchronised lock mechanism
Resuming a thread :threadObj.notifyAll() will release resume the object, after handling the synchronised lock mechanism
Thus based on this, i wrote a generic Thread class that is supposed to run a user's set of tasks and play/pause/resume/stop via ui buttons, BUT ITS NOT WORKING:
Generic Thread.java
public class PausibleThread extends Thread {
public static final String TAG ="PausibleThread>>";
#Nullable
PausibleRunnable runnable ;
public PausibleThread(#Nullable Runnable target) {
super(target);
PausibleRunnable r = new PausibleRunnable(target);
runnable=r;
}
#Override
public synchronized void start() { super.start(); }
public synchronized void stopThread(){ this.interrupt(); }
public synchronized void pauseThread(){ runnable.pause(); }
public synchronized void resumeThread(){ runnable.resume(); }
PausibleRunnable.java:
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class PausibleRunnable implements Runnable {
private Object lockerObject;
private boolean isPaused;
private boolean isFinished;
public static final String TAG="PausibleRunnable";
#Nullable
Runnable usrAction = null;
public PausibleRunnable(#NonNull Runnable usrAction) {
lockerObject = new Object();
isPaused = false;isFinished = false;
this.usrAction = usrAction;
}
public void run() {
while (!isFinished) {
if(isPaused) {
runPauseLoop();
}
else {
runUserAction();
isFinished=true;
}
}
}
private void runPauseLoop() {
synchronized (lockerObject) {
while (isPaused) {
try { lockerObject.wait(); }
catch (InterruptedException e) { e.printStackTrace(); }
}
}
}
private void runUserAction() {
if(usrAction !=null){ usrAction.run(); }
else { Log.e(TAG, "run: userAction is NULL" ); }
}
public void pause() {
synchronized (lockerObject) { isPaused = true; }
}
public void resume() {
synchronized (lockerObject) {
isPaused = false;
lockerObject.notifyAll();
}
}
}
Ui creating a Pausible Thread and implementing various functions of it:
//full class implementation at : https://paste.ubuntu.com/p/cTpW5Wt3Fy/
int totalRunTime = 20 * 5;
Pausible thread bgThread;
private void initThread() {
Runnable r = () -> {
try {
while (totalRunTime > 0) {
Thread.sleep(500);
totalRunTime--;
updateUi();
}
}
catch (Exception e) { e.printStackTrace(); }
};
bgThread = new PausibleThread(r);
}
private void updateUi() {
String data = "TotalRunTime=" + totalRunTime;
runOnUiThread(() -> tvTerminal.setText(data));
}
#Override
public void onClick(View v) {
if (bgThread == null) {
makeShortToast("Can't perform action, bg thread is null");
return;
}
if (v.getId() == fabPause.getId()) {bgThread.pauseThread(); }
else if (v.getId() == fabResume.getId()) { bgThread.resumeThread(); }
else if (v.getId() == fabStop.getId()) { bgThread.stopThread(); }
else if (v.getId() == fabStart.getId()) { bgThread.start(); }
}
But this does not work. Why? I am taking a wild guess here, but i think the runnable is only running user's action to run a big sized loop and not repeatedly checking for play/pause. So what am i supposed to do?
ui sample image : https://i.imgur.com/kmj3Bwt.png
You asked: "But this doesn't work. Why?"
I answer:
Your solution does not work because you are always running in the loop inside runUserAction. You never break out of that loop to check if you are paused.
I'm afraid you'll have to remodel your solution to run usrAction in shorter loops, otherwise you will either lose state (assuming you interrupt that loop from outside), which will end up in undefined behavior, OR you will only break out of it when it's over, OR you'll pause your loop at states you don't really want to pause at [e.g. while making a network call -- after resumed you'll get a SocketTimeoutException].
I'd suggest you to go with the former approach as it's more elegant.
Edit:
Another possible solution: every iteration inside the usrAction check for PausableThread's state, i.e. see whether it's paused, stopped or whatever.
Try this:
PausableRunnable.java
public synchronized boolean canContinue() throws Exception {
synchronized (lockerObject) {
if (isPaused) {
lockerObject.wait();
}
if (isFinished) {
return false;
}
return true;
}
}
PausableThread.java
public boolean canContinue() throws Exception {
return runnable.canContinue();
}
and the Application.java
private void initThread() {
Runnable r = () -> {
try {
while (totalRunTime > 0) {
if (bgThread.canContinue()) { // <--- !!!!!!
Thread.sleep(200);
totalRunTime--;
updateUi();
}
}
}
catch (Exception e) { e.printStackTrace(); }
};
bgThread = new PausibleThread(r);
}
This way you can run your application Runnable and still obey PausableThread's states at the times the runnable can tollereate. I.e. before/after transaction or other piece of calculation that is not supposed to be interrupted.
Edit 2:
feel free to lose ´synchronized´ modifier on methods like pause or resume, since you are already operating inside synchronized blocks in them.
so I have the following problem, I want to make a minigame on a text channel, the problem is, I want to create some sort of timeout so that people don't create multiple "listenerAdapter" instances that will just overload the bot.
the command I use to load my game event (ListenerAdapter is as follows).
#Override
public void handle(List<String> args, GuildMessageReceivedEvent event) {
// TODO Auto-generated method stub
TextChannel channel = event.getChannel();
channel.sendMessage("please type \"joingame\" to join! ").queue();
event.getJDA().addEventListener(new MinigameEvent(channel, event.getAuthor(), event));
}
then , the code I use for loading players in, is the following:
public class MinigameEvent extends ListenerAdapter {
private final long channelId, authorId;
private final int players=3;
private ArraySet<User> users;
private String textMsg;
private Message target;
private GuildMessageReceivedEvent outTimerEvent;
private boolean cancelEvent;
public MinigameEvent(MessageChannel channel, User author, GuildMessageReceivedEvent outTimerEvent) {
this.channelId = channel.getIdLong();
this.authorId = author.getIdLong();
this.outTimerEvent=outTimerEvent;
cancelEvent=false;
this.timeOut(channel);
users=new ArraySet<User>();
users.add(author);
textMsg=("registered users: "+author.getName());
channel.sendMessage(textMsg).queue((new Consumer<Message>()
{
#Override
public void accept(Message t)
{
target = t;
}
}));
}
#Override
public void onMessageReceived(MessageReceivedEvent event) {
if(event.getAuthor().isBot()) {
return;
}
//not respond on other channels
if (event.getChannel().getIdLong() != channelId) {
return;
}
MessageChannel channel = event.getChannel();
String content = event.getMessage().getContentRaw();
if(content.equalsIgnoreCase("joingame")) {
users.add(event.getAuthor());
textMsg=textMsg+", "+event.getAuthor().getName();
target.editMessage(textMsg).queue();
if(users.size()==players) {
event.getChannel().sendMessage("starting").queue();
event.getJDA().removeEventListener(this);
}
}
if(content.equalsIgnoreCase("cancel") && event.getAuthor().getIdLong()==authorId) {
cancelEvent=true;
event.getJDA().removeEventListener(this);
event.getChannel().sendMessage("this game has been canceled").queue();
}
}
private void timeOut(MessageChannel channel) {
Timer timer = new Timer();
TimerTask cooldown = new TimerTask() {
#Override
public void run() {
if(cancelEvent) {
return;
}
if(users.size()<players) {
outTimerEvent.getJDA().removeEventListener(this);
try {
destroyEvent();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
channel.sendMessage("not enough players, the game has been cancelled").queue();
}else {
return;
}
}
};
timer.schedule(cooldown, 10000L);
}
private void destroyEvent() throws Throwable {
this.finalize();
}
}
when I get to 3 people, the Listener adapter stops working as intended, also when the author of the event (the one who used the !minigame command) types cancel. but when the timer goes off, it sends the message indicating the game has been cancelled, but the listener adapter is still running, if someone tries to join after, it will allow him to do so.
I currently solved the issue by using the finalize method, but I thought that you could just do something like event.getJDA().removeEventListener(this);
Your problem is that your this refers to the nearest class declaration. In this case this is the anonymous class created by your new TimeTask() { .... To refer to the outer class which actually is registered as the listener you have to use MinigameEvent.this instead.
Read More
I would highly recommend using a lambda expression instead which doesn't have this problem. Another thing to note is your use of timer which will result in thread leaks because they are never shutdown by your code (How to cleanup a timer). Even better would be to use a single ScheduledExecutorService which you should re-use to schedule everything you need rather than creating a new one for every task. This can then be shutdown once your process ends (like the onShutdown event in JDA which is fired when shutdown() is called).
Apologies for the strangely worded title, but I'm running into an interesting concurrency issue in my testing. Here's the relevant code:
public class CancelableOperation {
boolean canceled;
boolean started;
public void start() {
if (!canceled) {
started = true;
// Kick off actual operation on another thread
}
}
public void cancel() {
if (!started) {
canceled = true;
} else {
// Attempt to cancel the other operation
}
}
}
#Test
public void test() {
CancelableOperation op = new CancelableOperation();
op.start();
while (!op.started) {
Thread.sleep(5);
}
op.cancel();
}
The issue is that cancel() gets called after started is true, but before the actual operation has kicked off on the new thread. In practice, it takes about 3 milliseconds for the operation to "actually" start, but that's more than enough time for cancel() to be called in my test. I can, of course, put a small Thread.sleep() in the test (after we've determined that op.started is true) to wait for the operation to begin, but I'd like to instead change my code to deal with this edge case.
Obviously, starting the operation on the secondary thread is the root of the problem, but, since the operation is a long, synchronous process, moving it onto this thread isn't feasible. Any help would be appreciated!
You can use CyclicBarrier
CyclicBarrier. A CyclicBarrier is a synchronizer that allows a set of
threads to wait for each other to reach a common execution point, also
called a barrier. CyclicBarriers are used in programs in which we have
a fixed number of threads that must wait for each other to reach a
common point before continuing execution.
So this code will helps you:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
class CancelableOperation {
boolean canceled;
boolean started;
public static final CyclicBarrier gate = new CyclicBarrier(2);
public CancelableOperation() {
gate.reset();
}
public void start() {
if (!canceled) {
System.out.println("started");
started = true;
try {
gate.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
// Kick off actual operation on another thread
}
}
public void cancel() {
try {
gate.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
if (!started) {
System.out.println("canceled");
canceled = true;
} else {
// Attempt to cancel the other operation
}
}
}
public class Test {
public static void main(String[] args) {
CancelableOperation op = new CancelableOperation();
new Thread(op::start).start();
new Thread(op::cancel).start();
}
}
So if cancel() reach to gate.await(); it will lock until start() reach to kick off another thread
I am trying to write a test for my android app that communicates with a cloud service.
Theoretically the flow for the test is supposed to be this:
Send request to the server in a worker thread
Wait for the response from the server
Check the response returned by the server
I am trying to use Espresso's IdlingResource class to accomplish that but it is not working as expected. Here's what I have so far
My Test:
#RunWith(AndroidJUnit4.class)
public class CloudManagerTest {
FirebaseOperationIdlingResource mIdlingResource;
#Before
public void setup() {
mIdlingResource = new FirebaseOperationIdlingResource();
Espresso.registerIdlingResources(mIdlingResource);
}
#Test
public void testAsyncOperation() {
Cloud.CLOUD_MANAGER.getDatabase().getCategories(new OperationResult<List<Category>>() {
#Override
public void onResult(boolean success, List<Category> result) {
mIdlingResource.onOperationEnded();
assertTrue(success);
assertNotNull(result);
}
});
mIdlingResource.onOperationStarted();
}
}
The FirebaseOperationIdlingResource
public class FirebaseOperationIdlingResource implements IdlingResource {
private boolean idleNow = true;
private ResourceCallback callback;
#Override
public String getName() {
return String.valueOf(System.currentTimeMillis());
}
public void onOperationStarted() {
idleNow = false;
}
public void onOperationEnded() {
idleNow = true;
if (callback != null) {
callback.onTransitionToIdle();
}
}
#Override
public boolean isIdleNow() {
synchronized (this) {
return idleNow;
}
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
this.callback = callback;
}}
When used with Espresso's view matchers the test is executed properly, the activity waits and then check the result.
However plain JUNIT4 assert methods are ignored and JUnit is not waiting for my cloud operation to complete.
Is is possible that IdlingResource only work with Espresso methods ? Or am I doing something wrong ?
I use Awaitility for something like that.
It has a very good guide, here is the basic idea:
Wherever you need to wait:
await().until(newUserIsAdded());
elsewhere:
private Callable<Boolean> newUserIsAdded() {
return new Callable<Boolean>() {
public Boolean call() throws Exception {
return userRepository.size() == 1; // The condition that must be fulfilled
}
};
}
I think this example is pretty similar to what you're doing, so save the result of your asynchronous operation to a field, and check it in the call() method.
Junit will not wait for async tasks to complete. You can use CountDownLatch to block the thread, until you receive response from server or timeout.
Countdown latch is a simple yet elegant solution and does NOT need an external library. It also helps you focus on the actual logic to be tested rather than over-engineering the async wait or waiting for a response
void testBackgroundJob() {
Latch latch = new CountDownLatch(1);
//Do your async job
Service.doSomething(new Callback() {
#Override
public void onResponse(){
ACTUAL_RESULT = SUCCESS;
latch.countDown(); // notify the count down latch
// assertEquals(..
}
});
//Wait for api response async
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
assertEquals(expectedResult, ACTUAL_RESULT);
}
So I have SparkleAcrivator class
public class SparkleActivator {
private static boolean sparkleLibLoaded = false;
//private String downloadLink;
private String menuItemTitle;
public native static void initSparkle(String pathToSparkleFramework,
boolean updateAtStartup,
int checkInterval,
/*String downloadLink,*/
String menuItemTitle);
private boolean updateAtStartup = true;
private int checkInterval = 86400;
public SparkleActivator(/*String downloadLink, */String menuItemTitle) {
//this.downloadLink = downloadLink;
this.menuItemTitle = menuItemTitle;
}
public void start() throws Exception {
try {
if(!SparkleActivator.sparkleLibLoaded) {
System.loadLibrary("sparkle_init");
SparkleActivator.sparkleLibLoaded = true;
}
} catch (Exception ex) {
ex.printStackTrace();
return;
}
initSparkle(System.getProperty("user.dir") + "/../../Frameworks/Sparkle.framework",
updateAtStartup, checkInterval, /*downloadLink, */menuItemTitle);
}
}
And main class where I start my standalone application and use Sparkle
public static void main(final String... args) {
if (Helper.isOsx()) {
try {
sparkleActivator.start();
} catch (Exception e) {
new ExceptionHandler(true, 19).handleException(new NotFountSparkleInitException());
return;
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
initApp();
}
});
}
else {
}
There problem is Sparkle and my app starts almost simultaneously, but I need wait for action from user in Sparkle window and then start my app.
Thank for any help.
What we know is that Sparkle updater requires to be called on the main thread (i.e. GUI thread). Therefore, your app needs to be running to call it. I don't think there is an obvious way to solve this problem. However,...
One way that you can circumvent the issue is to set your app's visibility to false until the user has finished interacting with your updater.
For example, you can add an event listener to your JNI or add listeners to your updater thread. However, the listener on the thread needs to return on user actions instead of when finished.