Check if RunConfiguration is startedI

Answered

I'm my plugin, I need to start a RunConfiguration by code (I asked how to do it here: https://intellij-support.jetbrains.com/hc/en-us/community/posts/360002754820-Start-a-Run-Configuration-from-code ).

Now, let's say that my RunConfiguration is running, how can I check via code that it's state (e.g. it is running) by having the instance of the RunConfiguration (or the RunnerAndConfigurationSettings for that RunConfiguration)?.

I've found elsewhere that I can use ExecutionManager.getRunningProcesses(), but then I don't see where I can understand if the ProcessHandler is for a specific RunConfiguration.

Any hint?

Best
Michele

18 comments
Avatar
Vassiliy Kudryashov
Comment actions Permalink

It is not a part of public API but still you can try to use ExecutionManagerImpl.getRunningDescriptors(Condition)

0
Comment actions Permalink

Hi Michele and Vassiliy,

I would like to do exactly the same thing. I have my own RunnerAndConfigurationSettingsImpl launchAppConfigSettings and would like to check if it's running.

Based on your answer, I have tried the code below, but am not sure what to do with the return of .getRunningDescriptors().

ExecutionManagerImpl executionManager = ExecutionManagerImpl.getInstance(project);

Executor executor = DefaultRunExecutor.getRunExecutorInstance();

runConfiguration(launchAppConfigSettings, executor);

contentDescriptors = executionManager.getRunningDescriptors(s -> s == launchAppConfigSettings);

I think there may be a problem in my condition. Thank you for any advice.



0
Avatar
Vassiliy Kudryashov
Comment actions Permalink

@Richard, you're checking for running descriptors too early, just after calling runConfiguration(...)

But runConfiguration(...) method is not synchronous.

0
Comment actions Permalink

@Vassily, thank you. I now have a thread using .getRunningDescriptors to wait until the Run Configuration finishes. This works, but is there a better way of doing it? Thanks.

Thread t_checkRunning = (new Thread(new Runnable() {
@Override
public synchronized void run() {

try {
while (executionManager.getRunningDescriptors(s -> s == launchAppConfigSettings).isEmpty()) {

this.wait(1000);
}
while (! executionManager.getRunningDescriptors(s -> s == launchAppConfigSettings).isEmpty()) {

this.wait(1000);
}

// Perform code after run configuration here...

} catch (Exception e) { ... }
}
}
0
Avatar
Vassiliy Kudryashov
Comment actions Permalink

Why do you need this?

Please check if code like this helps you:

{someProject}.getMessageBus().connect({someDisposable}).subscribe(ExecutionManager.EXECUTION_TOPIC, new ExecutionListener() {
@Override
public void processStarted(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler) {
//check if started process is needed one
}

@Override
public void processTerminated(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler, int exitCode) {
//check if terminated process is needed one
}
});
0
Comment actions Permalink

Thank you Vassily, that is exactly what i was looking for, but wasn't sure how to imlement the Listener. It works like a charm, thanks again!

0
Comment actions Permalink

Hi Vassily, I have one more question. How can I find out if it was my custom run configuration that terminated in processTerminated using

executionManager.getRunningDescriptors(s -> s == launchAppConfigSettings)

if the running descriptors are empty because the process just terminated?

In case my question isn't clear, this is what I am trying to do, which obviously will not work.

@Override
public void processTerminated(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler, int exitCode) {
//check if terminated process is needed one

if (! executionManager.getRunningDescriptors(s -> s == launchAppConfigSettings).isEmpty()) {
System.out.println("My Process just terminated, time to do other things.");
}

}

Thank you

0
Avatar
Vassiliy Kudryashov
Comment actions Permalink

It's better to use ProcessHander or ExecutionEnvironment to check what exactly is terminated because several instances of the same RunnerAndConfigurationSettings can run at the same time. Try to remember processHandler or at least System.identityhashCode(processHandler) when your process started and use it when you check which process is terminated.

0
Comment actions Permalink

Thanks Vassiliy, I wasn't able to figure out how to use the ProcessHandler but I managed to get it to work by simply storing the hashCode of my custom RunnertAndConfigurationSettings and then comparing against it in the ExecutionListener.

0
Comment actions Permalink

Hi @Vassiliy, one more question - How can I remove or unsubscribe the ExecutionListener? Thanks

0
Avatar
Vassiliy Kudryashov
Comment actions Permalink

See example above:

{someProject}.getMessageBus().connect({someDisposable}).subscribe(...

here disposable makes all magic with un-subscription.

(see also https://github.com/JetBrains/intellij-community/blob/master/platform/util/src/com/intellij/openapi/Disposable.java)

0
Comment actions Permalink

I thought it had something to do with the Disposable. I am sorry, I have never worked with Disposables before.
Do I not still need to unsubscribe in the dispose() method? And do I need to register it with the Disposer?
Can I please ask you for a code snippet to help me along and let me complete this faster? Thank you very much :)

0
Avatar
Vassiliy Kudryashov
Comment actions Permalink

"Project" is good example of disposable. But in your case real lifecycle of listener might be shorter than project's lifecycle.

Sure you have to register disposable with Disposer (and some existing parent disposable) if you create new one

OR just specify existing disposable (e.g. project instance) on subscription.

0
Comment actions Permalink

Right, I understand, thank you for the explanantion. Is it possible to dispose of the Listener the moment processTerminated() is called?

0
Avatar
Vassiliy Kudryashov
Comment actions Permalink

Sure, you have to call Disposer.dispose({yourDisposable}) in this case. Don't dispose project :-)

0
Comment actions Permalink

Hi Vassiliy, I have spent a lot of time thinking about this (and I realise you have too by always responding to me, so thank you) but I just cannot get my head around what I should use as a Disposable. I'll explain what I am trying to do and maybe you can offer some advice.

I have AnAction that runs the currently open app and needs to do something when the user closes the app on their Android phone. I am using the code above to run a RunConfiguration. Since runConfiguration() is asynchronous, the AnAction method will come to an end at that point. Using the Listener code you provided, I have been able to do something after the app is closed. However, this doesn't work well when you try to run this Action mutliple times (not at the same time) because only one Listener can be added to the connection. I open a new Content in my ToolWindow with every execution of AnAction, and this way the first Listener is always linked to the first Content, so any following executions of AnAction do not display my output correctly.

A solution is to dispose of this Listener, or connection, when it is triggered. Another would be (if possible) to have multiple unique Listeners that are each linked to their correct Content in the ToolWindow.

I think I grasp Disposable now, but in this situation, I do not know what ParentDisposable to link the Listener to. The Project's lifespan is too long and the AnAction class' is too short.

0
Avatar
Vassiliy Kudryashov
Comment actions Permalink

As for disposable, just try to create new one: com.intellij.openapi.util.Disposer#newDisposable() and call Diposer.dispose in processTerminated() method. Also this new disposable instance is good to open new connection with.

As parent you can specify project if you don't have better candidates with 'better' lifecycle. Also I recommend you overriding

com.intellij.execution.ExecutionListener#processNotStarted

to call Disposer.dispose() in this case too.

0

Please sign in to leave a comment.