How to restore state machine from context builded in runtime? - java

I have a state machine
#EnableStateMachine
#Configuration
public class StateMachineConfiguration extends EnumStateMachineConfigurerAdapter<Status, Event> {
#Override
public void configure(StateMachineStateConfigurer<Status, Event> states) throws Exception {
states.withStates()
.initial(Status.DRAFT)
.states(EnumSet.allOf(Status.class));
}
#Override
public void configure(StateMachineTransitionConfigurer<Status, Event> transitions) throws Exception {
transitions
.withExternal()
.target(Status.INVITATION).source(Status.DRAFT)
.event(Event.INVITED)
.guard(new Guard())
.action(new ActionInvited())
.and()
.withExternal()
.target(Status.DECLINED).source(Status.INVITATION)
.event(Event.DECLINED)
.action(new ActionDeclined());
}
#Override
public void configure(StateMachineConfigurationConfigurer<Status, Event> config) throws Exception {
config.withConfiguration().autoStartup(true);
}
}
and I have a model, for example Order.
Model persists in DB. I extract model from DB, now my model has a status Order.status == INVITATION. I want to continue processing model with statemachine, but instance of statemachine will starts processing with initial state DRAFT but I needs continue processing from status INVITATION. In other words I want to execute
stateMachine.sendEvent(MessageBuilder
.withPayload(Event.DECLINED)
.setHeader("orderId", order.id)
.build()
)
and execute action ActionDeclined(). I don't want to persist a context of state machine in DB. I want to setting a state of stateMachine to state of my Model in runtime. How can I do that in right way? Using DefaultStateContext constructor or have an other, more beautiful way?

One possible approach is to create the StateMachine on the fly and to rehydrate the state machine from the DB using the state of the Order.
In this case you need to do the following steps:
Resetting the StateMachine in all regions
Load Order status from DB
Create new DefaultStateMachineContext and populate accordingly
Let's assume you have a build method, which returns new state machines for processing order events (using a StateMachineFactory), but for an existing order, it will rehydrate the state from the database.
StateMachine<Status, Event> build(long orderId) {
orderService.getOrder(orderId) //returns Optional
.map(order -> {
StateMachine<Status, Event> sm = stateMachineFactory.getStateMachine(Long.toString(orderId));
sm.stop();
rehydrateState(sm, sm.getExtendedState(), order.getStatus());
sm.start();
return sm;
})
.orElseGet(() -> createNewStateMachine(orderId);
}
void rehydrateState(StateMachine<Status, Event> newStateMachine, ExtendedState extendedState, Status orderStatus) {
newStateMachine.getStateMachineAccessor().doWithAllRegions(sma ->
sma.resetStateMachine(new DefaultStateMachineContext<>(orderStatus, null, null, extendedState));
});
}

Related

Updating a processed item in Spring Batch

I have a Spring Batch app that uses ItemProcessor to process items.
#Component
#StepScope
public class MemberProcessor<T> implements ItemProcessor<T, Member> {
#Override
public Member process(T t) throws Exception {
return processMember((UnprocessedMember) t);
}
}
#Component
#StepScope
public class MemberWriter implements ItemWriter<Member> {
#Override
public void write(List<? extends Member> members) throws Exception {
//store the members in db
saveToDb(members);
}
}
I want to know if it is possible to update an item after it's been processed so that when it gets to ItemWriter, it's updated. For example, I process one item, and then I process another one that may need to edit a property of the previous item. As the previous item has not reached the Writer, I can't do the update on the database and the first item gets written without the update
Spring Batch provides ItemProcesListener for the processing of an item before items processed by the writer. Implementations of this interface will be notified before and after an item is passed to the ItemProcessor and in the event of any exceptions thrown by the processor.
So basically you need to create a custom item process listener implementing ItemProcesListener and register with the processor setp task.
Example:
public class MyCustomItemProcessListener implements ItemProcessListener<T, R> {
#Override
public void beforeProcess(T item) {
System.out.println("MyCustomItemProcessListener - beforeProcess");
}
#Override
public void afterProcess(T item, R result) {
System.out.println("MyCustomItemProcessListener - afterProcess");
// Apply your custom logic to act on the object before write
}
#Override
public void onProcessError(T item, Exception e) {
System.out.println("MyCustomItemProcessListener - onProcessError");
}
}
Now you can add the listener to your step. This depends on your Step definitions and configurations. But essentially something like the below.
StepBuilderFactory.get("Stepx")
...
.processor(someProcessor).listener(new MyCustomItemProcessListener())
...
Or
steps.get("stepx")
.tasklet(new MyProcessorTask())
.listener(new MyCustomItemProcessListener())
.build();

How to add dynamic parameters to spring state machine action?

I have this simple state machine configuration :
#Configuration
#EnableStateMachine
public class SimpleStateMachineConfiguration extends StateMachineConfigurerAdapter<State, Boolean> {
#Override
public void configure(StateMachineStateConfigurer<State, Boolean> states) throws Exception {
states.withStates()
.initial(State.INITIAL)
.states(EnumSet.allOf(State.class));
}
#Override
public void configure(StateMachineTransitionConfigurer<State, Boolean> transitions) throws Exception {
transitions
.withExternal()
.source(State.INITIAL)
.target(State.HAS_CUSTOMER_NUMBER)
.event(true)
.action(retrieveCustomerAction())
// here I'd like to retrieve the customer from this action, like:
// stateMachine.start();
// stateMachine.sendEvent(true);
// stateMachine.retrieveCustomerFromAction();
.and()
.withExternal()
.source(State.INITIAL)
.target(State.NO_CUSTOMER_NUMBER)
.event(false)
.action(createCustomerAction());
// here I'd like to send the customer instance to create, like:
// stateMachine.start();
// stateMachine.sendEvent(false);
// stateMachine.sendCustomerToAction(Customer customer);
}
#Bean
public Action<State, Boolean> retrieveCustomerAction() {
return ctx -> System.out.println(ctx.getTarget().getId());
}
#Bean
public Action<State, Boolean> createCustomerAction() {
return ctx -> System.out.println(ctx.getTarget().getId());
}
}
Is it possible to improve actions definition to be able to interact with them with dynamics parameters ?
How could I add consumer or provider behaviors to those actions ?
Is it possible to improve actions definition to be able to interact
with them with dynamics parameters?
Yes, it's possible. You can store the variables in the context store and retrieve then wherever you want.
public class Test {
#Autowired
StateMachine<State, Boolean> stateMachine;
public void testMethod() {
stateMachine.getExtendedState().getVariables().put(key, value);
stateMachine.start();
stateMachine.sendEvent(true);
}
}
And You can retrieve this value from the context using the key. Suppose the value was of the type String then it can be retrieved like this:-
#Bean
public Action<State, Boolean> retrieveCustomerAction() {
return ctx -> {
String value = ctx.getExtendedState().get(key, String.class);
// Do Something
};
}
For more you can refer the link and this
How could I add consumer or provider behaviors to those actions?
Can you elaborate more on this question

Why does my Spring #EventListener show different transactional behavior on event submission than when being called directly?

When using the #EventListener functionality with Spring Data's repositories the behavior is different than when calling the same code procedural.
My persistent objects publish events using the following base class:
public abstract class Aggregate {
#Transient
private transient final Set<Object> events = new LinkedHashSet<>();
protected <T> T registerEvent(T event) {
this.events.add(event);
return event;
}
#DomainEvents
Collection<Object> events() {
return Collections.unmodifiableSet(events);
}
#AfterDomainEventPublication
void clearEvents() {
this.events.clear();
}
}
My event listening class is implemented as follows:
class Service {
#EventListener
public void listener(SomeEvent event) {
someOtherRepository.save(someOtherPersistentObject);
someOtherCode();
}
}
When the listener is triggered and someOtherRepository's save(…) method fails a rollback will be issued. But someOtherCode() is executed regardless of the rollback.
But when I remove all #EventListening functionality and call the listener(…) method directly after the point where the originating repository is responsible for firing the event. Then I get a different behavior. Then someOtherCode() is never executed and the someOtherRepository.save(…) method fails immediately.
The original service responsible for publishing the event looks like this
public OriginatingService {
#Transactional
public void someMethod() {
originatingRepoDifferentFromSomeOtherRepo.save(something);
Why is this happening and is there a way to force the same behavior onto my event listening implementation?
Because writes to the database may be delayed until transaction commit i.e. when the transactional method returns.
Update as below to explicitly trigger an immediate flush:
#EventListener
public void listener(SomeEvent event) {
someOtherRepository.saveAndFlush(someOtherPersistentObject);
someOtherCode();
}

Play! framework: get controller's component/field/object between requests

I'm new to Play! and during working within a project I came with the follow situation:
I have a controller HomeController which has two main actions:
public CompletationStage<Result> search(String query){
return docSearcher.search(query).thenApplyAsync((json) -> {
return ok(json);
}, ec);
}
public CompletationStage<Result> zoom(){
return docSearcher.zoom().thenApplyAsync((json) -> {
return ok(json);
}, ec);
}
The search action receives a query parameter and delegates to docSearcher to do a DB search (docSearcher is injected into the controller).
The zoom action should provide a zoom in/out in the visualization redered as HTML.
The problem is: the docSearcher constructor creates a instance of a QuadTree which will be used to perform zoom efficiently, so this QuadTree instance should be available to zoom action after calling search action. The way it is, a single QuadTree in docSearcher is shared among clients/threads. I would like to have a QuadTree per search action request and share this new created QuadTree with subsequent zoom actions.
How can I achieve it using Play!? I'm not sure how can I store user data (QuadTree depends of user query and I don't want to build a new tree for each request, I want to share QuadTrees among search/zoom requests).
Thanks.
Follow the code of docSearcher class:
public class LocalDocumentSearcher implements DocumentSearcher {
private QuadTree quadTree;
#Inject
public LocalDocumentSearcher(Database db, DatabaseExecutionContext context) {
this.executionContext = context;
this.quadTree = initQuadTree();
}
#Override
public CompletionStage<String> search(String query) throws Exception {
return CompletableFuture.supplyAsync(() -> {
return searchData(queryData);
}, executionContext);
}
#Override
public CompletionStage<String> zoom() throws Exception {
return CompletableFuture.supplyAsync(() -> {
return zoomData();
}, executionContext);
}

Get spring transaction by it's ID and check status

Is there any way to get current transaction id, store/pass it, and check it's status in the another part of application?
For example:
#Service
public class Service {
...
#Transactional(rollbackFor = Exception.class)
public void performAction(Action action) {
// start action
String transactionId = ??? // getting current transaction id
messenger.send(transactionId); // sending transaction id to consumer
// continue action
} <- commit transaction
}
public class Consumer {
...
public void onRecieveMessage(String transactionId) {
TransactionStatus ts = ??? // getting transaction from pool by id
if (ts.isCompleted()) {
// some actions
} else {
// wating or Future<?> or something else...
}
}
}
(Actually, the problem is Consumer.onRecieveMessage executes earlier than action's transaction finishes, and data state is old)
I do not know any way of getting a specific transaction by its ID (hopefully someone does).
What I would do is to implement an instance of TransactionSynchronization that holds a kind of value you want to track:
public class YourTransactionTracker extends TransactionSynchronizationAdapter {
private Value value;
public YourTransactionTracker(Value value) {
this.value = value;
}
#Override
public void afterCompletion(int status) {
//transaction handling your 'value' has been completed with the status 'status'
//do whatever you want here
}
//you can also override other methods like beforeCompletion(), flush(), etc...
//have a look at TransactionSynchronizationAdapter
}
TransactionSynchronizationAdapter is just an implementation of TransactionSynchronization with empty methods.
Then you can hook a callback in the transaction with your TransactionSynchronization instance, that will be called in the different stages of the lifecycle of the transaction.
You can do this in Spring by using the TransactionSynchronizationManager:
TransactionSynchronizationManager.registerSynchronization(
new YourTransactionTracker(theValueYouAreTracking);

Categories

Resources