How to get taskid after the complete method - java

I have a workflow like: startevent -> task1(Assignee:Tom) -> choose sequence flow "agree" ->task2(Assignee:Jerry) -> choose sequence flow "disagree" -> task1
When the flow arrive to task1, i want to set assignee to "Tom" again.
Now i have an idea like:
When the flow arrive to task1, i use complete method, after the complete method, set a local variable "pre_task_id(task1's taskid)" in task2 so that i can use task1's taskid to search in "act_hi_taskinst" table for assignee(Tom), but this method taskService.setVariableLocal(taskId, variableName, value) need task2's taskid, how can i get the task2's taskid after complete method?
#Test
public void testCompleteTask() {
Task task = taskService.createTaskQuery().taskAssignee("Tom").singleResult();
if (task == null) {
System.out.println("no task!!!");
return;
}
String preTaskId = task.getId();
HashMap <String,Object> variables = new HashMap<>();
variables.put("userId", "Jerry");
variables.put("oper", "saolu");
taskService.complete(task.getId(),variables);
//don't konw how to get the taskId
//taskService.setVariableLocal(taskId, "pre_task_id", preTaskId);
}
I am using activiti6
Or please let me konw if there are any better solutions
workflow.png

If I understand properly. You need to keep track which task is executed most recently.
In that case you can use a stack. When ever you execute any task you just call the push with the task id.
When ever you call pop it will give you the task id of the most recent task executed.
Just be careful about how you want to clear the stack as well.
I would recommend to always call pop before calling push.

In the past I have always used a process variable for this purpose, Have never attempted the "push" operation suggested above but I would be concerned about getting the correct value back if multiple parallel processes are all pushing/pulling to and from the stack at once. The process variable is simple and I know it works.

Related

Thread safety for method that returns Mono based on mutable attribute in Java

In my Spring Boot application I have a component that is supposed to monitor the health status of another, external system. This component also offers a public method that reactive chains can subscribe to in order to wait for the external system to be up.
#Component
public class ExternalHealthChecker {
private static final Logger LOG = LoggerFactory.getLogger(ExternalHealthChecker.class);
private final WebClient externalSystemWebClient = WebClient.builder().build(); // config omitted
private volatile boolean isUp = true;
private volatile CompletableFuture<String> completeWhenUp = new CompletableFuture<>();
#Scheduled(cron = "0/10 * * ? * *")
private void checkExternalSystemHealth() {
webClient.get() //
.uri("/health") //
.retrieve() //
.bodyToMono(Void.class) //
.doOnError(this::handleHealthCheckError) //
.doOnSuccess(nothing -> this.handleHealthCheckSuccess()) //
.subscribe(); //
}
private void handleHealthCheckError(final Throwable error) {
if (this.isUp) {
LOG.error("External System is now DOWN. Health check failed: {}.", error.getMessage());
}
this.isUp = false;
}
private void handleHealthCheckSuccess() {
// the status changed from down -> up, which has to complete the future that might be currently waited on
if (!this.isUp) {
LOG.warn("External System is now UP again.");
this.isUp = true;
this.completeWhenUp.complete("UP");
this.completeWhenUp = new CompletableFuture<>();
}
}
public Mono<String> waitForExternalSystemUPStatus() {
if (this.isUp) {
LOG.info("External System is already UP!");
return Mono.empty();
} else {
LOG.warn("External System is DOWN. Requesting process can now wait for UP status!");
return Mono.fromFuture(completeWhenUp);
}
}
}
The method waitForExternalSystemUPStatus is public and may be called from many, different threads. The idea behind this is to provide some of the reactive flux chains in the application a method of pausing their processing until the external system is up. These chains cannot process their elements when the external system is down.
someFlux
.doOnNext(record -> LOG.info("Next element")
.delayUntil(record -> externalHealthChecker.waitForExternalSystemUPStatus())
... // starting processing
The issue here is that I can't really wrap my head around which part of this code needs to be synchronised. I think there should not be an issue with multiple threads calling waitForExternalSystemUPStatusat the same time, as this method is not writing anything. So I feel like this method does not need to be synchronised. However, the method annotated with #Scheduled will also run on it's own thread and will in-fact write the value of isUp and also potentially change the reference of completeWhenUpto a new, uncompleted future instance. I have marked these two mutable attributes with volatilebecause from reading about this keyword in Java it feels to me like it would help with guaranteeing that the threads reading these two values see the latest value. However, I am unsure if I also need to add synchronized keywords to part of the code. I am also unsure if the synchronized keyword plays well with reactor code, I have a hard time finding information on this. Maybe there is also a way of providing the functionality of the ExternalHealthCheckerin a more complete, reactive way, but I cannot think of any.
I'd strongly advise against this approach. The problem with threaded code like this is it becomes immensely difficult to follow & reason about. I think you'd at least need to synchronise the parts of handleHealthCheckSuccess() and waitForExternalSystemUPStatus() that reference your completeWhenUp field otherwise you could have a race hazard on your hands (only one writes to it, but it might be read out-of-order after that write) - but there could well be something else I'm missing, and if so it may show as one of these annoying "one in a million" type bugs that's almost impossible to pin down.
There should be a much more reliable & simple way of achieving this though. Instead of using the Spring scheduler, I'd create a flux when your ExternalHealthChecker component is created as follows:
healthCheckStream = Flux.interval(Duration.ofMinutes(10))
.flatMap(i ->
webClient.get().uri("/health")
.retrieve()
.bodyToMono(String.class)
.map(s -> true)
.onErrorResume(e -> Mono.just(false)))
.cache(1);
...where healthCheckStream is a field of type Flux<Boolean>. (Note it doesn't need to be volatile, as you'll never replace it so cross-thread worries don't apply - it's the same stream that will be updated with different results every 10 minutes based on the healthcheck status, whatever thread you'll access it from.)
This essentially creates a stream of healthcheck response values every 10 minutes, always caches the latest response, and turns it into a hot source. This means that the "nothing happens until you subscribe" doesn't apply in this case - the flux will start executing immediately, and any new subscribers that come in on any thread will always get the latest result, be that a pass or a fail. handleHealthCheckSuccess() and handleHealthCheckError(), isUp, and completeWhenUp are then all redundant, they can go - and then your waitForExternalSystemUPStatus() can just become a single line:
return healthCheckStream.filter(x -> x).next();
...then job done, you can call that from anywhere and you'll have a Mono that will only complete when the system is up.

is `CompletableFuture.completedFuture ... thenAccept` equivalent to sequential processing?

I'm working on a project with a lot of CompletableFuture.completedFuture ... thenAccept codes, e.g.
public CompletableFuture<Boolean> callee() {
boolean result = ... // Do something and get result - Step A
return CompletableFuture.completedFuture(Boolean.valueOf(result));
}
public void caller() {
callee().thenAccept(result -> {
// Detect if call success or failure - Step B
new Throwable().printStackTrace(); // the debug code: stacktrace shows it is called from caller
});
}
I concluded that Step A and Step B are called sequentially in one thread.
So can I simplify it like this?
public boolean callee() {
boolean result = ... // Do something and get result
return result;
}
public void caller() {
boolean result = callee();
// Detect if call success or failure
}
Yes, you can simplify it like this. The long version:
I think the question should be rather: "Is this usage of CompletableFuture appropriate?". No, it's not. This code is using CompletableFuture like a wrapper, a package, to pass data around and not as a tool to execute code asynchronously. This tool can be used to pass data around between threads, but it's not what this code is doing.
Calling CompletableFuture.completedFuture does nothing but create a new CompletableFuture that is completed with whatever you pass to the method. Then you call thenAccept on it, which has basically the following effect: "Take the result when it's done and let the thread that has calculated the result execute the following code. If the result is already calculated, let the caller execute the following code themself." The "following code" is simply the lambda you pass to thenAccept.
The initial CompletableFuture is completed instantly and the following code gets executed by the thread that calls thenAccept directly. The thread that executes caller and callee does everything itself. So this part is effectively doing nothing asynchronously. Therefore, the code is equivalent to the simpler code in the second example without CompletableFuture.
To actually make use of CompletableFuture, you should run boolean result = ... // Do something and get result - Step A asynchronously by e.g. creating this initial future using CompletableFuture.supplyAsync. The chained code will also be run asynchronously.

List all the tasks for a given Process in the order of execution

This is a basic functionality and I see repeated questions , but unfortunately no clear answer yet.
How do I print/list all the tasks in the given process ( finished / unfinished ) in the order of execution.
The two solution I found on the forum are working as expected
repositoryService.getBpmnModel().getFlowElements() - Does not print in the order of execution . Printed in the order of definition
historyService.createHistoricActivityQuery - Does not print all Service task
How do I just list all the task under the given process.
If by tasks you mean all the elements in the process then you can use the HistoricActivityInstanceQuery to get the information about them.
The code would look something like:
List<HistoricActivityInstance> activityInstances = historyService
.createHistoricActivityInstanceQuery().
.processInstanceId(processInstanceId)
.orderByHistoricActivityInstanceStartTime().asc()
.list();
In order to see if a HistoricActivityInstance is finished or not you'll need to check the HistoricActivityInstance#getEndTime(). When that is null it means that the activity is not finished, if it is null then it means it is finished.
You can create a TaskQuery
import org.camunda.bpm.engine.ProcessEngine;
...
#Autowired
private ProcessEngine processEngine;
private List<Task> getAllTaskByProcessId(string processInstanceId){
return processEngine.getTaskService()
.createTaskQuery()
.processInstanceId(processInstanceId)
.list();
}

Using the faster output from 2 threads

I want to work with two threads in my Java program for a tiny part. I need to give the first call to a database and the second call to an API, both calls with same input, and then work with the output of whichever thread finishes first.
It's my first time programming with threads and I'm very confused. I've seen tutorials and they mainly explain how to get two separate things done with threads so I'm a little lost.
Can someone please help or re-direct me to any useful link they may have?
So far, as I understand it, should it look something like this? :
Thread thread1 = new Thread(func1());
Thread thread2 = new Thread(func2());
thread1.start();
thread2.start();
But then how do I extract the output of the functions? How would I know which one has finished first?
-----------UPDATE 1---------
After trying CompletableFuture (thanks for the help Johan!) I have something like this:
CompletableFuture<Object> getData = CompletableFuture.anyOf(
CompletableFuture.runAsync(() -> getDataFromDB(clientData)),
CompletableFuture.runAsync(() -> getDataFromApi(clientData))
);
getData.thenApply(dataObject -> {
// Cast the returned Object to the actual type of your data,
// assuming both getDataFromDb and getDataFromApi
// return the same result type
Object data = (String) dataObject;
// Work with the returned data.
result = (String) data;
});
But I get this error for getData.thenApply():
The method thenApply(Function) in the type CompletableFuture is not applicable for the arguments (( dataObject) -> {})
Since I know that getData in of type String, would it be okay to just convert it to String and store the result?
As #Johan Hirsch suggests try with CompletableFuture. I've just try this and it works:
CompletableFuture.anyOf(
CompletableFuture.supplyAsync(() -> getDataFromDB(clientData)),
CompletableFuture.supplyAsync(() -> getDataFromApi(clientData)))
.thenApply(item -> (String) item)
.thenAccept(result -> {
// Consume the data
System.out.println(result);
});
Beware that I'm currently consuming the data so it doesn't return anything. If you just want to pass the result to another CompletableFuture change the thenAccept method for a thenApply
Java 8 provides a very nice utility class called CompletableFuture, which can help in your case.
Create two CompletableFuture, one for each of your tasks, and then use the CompletableFuture.anyOf method to wait for either one to finish.
CompletableFuture<TData> getData = CompletableFuture.anyOf(
CompletableFuture.runAsync(() -> getDataFromDb()),
CompletableFuture.runAsync(() -> getDataFromApi())
);
getData.thenApply(dataObject -> {
// Cast the returned Object to the actual type of your data,
// assuming both getDataFromDb and getDataFromApi
// return the same result type
TData data = (TData)dataObject;
// Work with the returned data.
processData(data);
});
You can use ExecutorService.invokeAny
Executes the given tasks, returning the result of one that has completed successfully (i.e., without throwing an exception), if any do. Upon normal or exceptional return, tasks that have not completed are cancelled. The results of this method are undefined if the given collection is modified while this operation is in progress.

Code running on main thread even with subscribeOn specified

I'm in the process of migrating an AsyncTaskLoader to RxJava, trying to understand all the details about the RxJava approach to concurrency. Simple things were running ok, however I'm struggling with the following code:
This is the top level method that gets executed:
mCompositeDisposable.add(mDataRepository
.getStuff()
.subscribeOn(mSchedulerProvider.io())
.subscribeWith(...)
mDataRepository.getStuff() looks like this:
public Observable<StuffResult> getStuff() {
return mDataManager
.listStuff()
.flatMap(stuff -> Observable.just(new StuffResult(stuff)))
.onErrorReturn(throwable -> new StuffResult(null));
And the final layer:
public Observable<Stuff> listStuff() {
Log.d(TAG, ".listStuff() - "+Thread.currentThread().getName());
String sql = <...>;
return mBriteDatabase.createQuery(Stuff.TABLE_NAME, sql).mapToList(mStuffMapper);
}
So with the code above, the log will print out .listStuff() - main, which is not exactly what I'm looking for. And I'm not really sure why. I was under impression that by setting subscribeOn, every event pulled from the chain will be processed on the thread specified in the subscribeOn method.
What I think is happening, is that the source-aka-final-layer code, before reaching mBriteDatabase, is not from the RxJava world and therefore is not an event until createQuery is called. So I probably need some sort of a wrapper? I've tried applying .fromCallable, however that's a wrapper for non Rx code, and my database layer returns an observable...
Your Log.d call happens
immediately when listStuff gets called
which is immediately after getStuff gets called
which is the first thing happening in the top level code fragment you show us.
If you need to do it when the subscription happens, you need to be explicit:
public Observable<Stuff> listStuff() {
String sql = <...>;
return mBriteDatabase.createQuery(Stuff.TABLE_NAME, sql)
.mapToList(mStuffMapper)
.doOnsubscribe(() -> Log.d(TAG, ".listStuff() - "+Thread.currentThread().getName()));
}

Categories