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

0
18 comments
Avatar
Permanently deleted user

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

0
Avatar
Permanently deleted user

Super, thanks!

0
Avatar
Permanently deleted user

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
Permanently deleted user

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

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

0
Avatar
Permanently deleted user

@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
Permanently deleted user

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
Avatar
Permanently deleted user

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
Avatar
Permanently deleted user

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
Permanently deleted user

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
Avatar
Permanently deleted user

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
Avatar
Permanently deleted user

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

0
Avatar
Permanently deleted user

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
Avatar
Permanently deleted user

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
Permanently deleted user

"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
Avatar
Permanently deleted user

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

0
Avatar
Permanently deleted user

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

0
Avatar
Permanently deleted user

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
Permanently deleted user

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.