SpringBoot StateMachine etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
SpringBoot StateMachine etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

24 Ekim 2021 Pazar

SpringBoot StateMachine EnumStateMachineConfigurerAdapter Sınıfı

Giriş
Şu satırı dahil ederiz
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
İki tane configure metodu var. Biri states diğeri transitions için

configure metodu
Örnek
Şöyle yaparız
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends 
  EnumStateMachineConfigurerAdapter<OrderState,OrderEvent> {

  @Override
  public void configure(StateMachineStateConfigurer<OrderState,OrderEvent> states)
  throws Exception {
    states.withStates()
      .initial(OrderState.ORDERED)
      .states(EnumSet.allOf(OrderState.class));
  }

  @Override
  public void configure(StateMachineTransitionConfigurer<OrderState,OrderEvent> transitions)
  throws Exception {
    transitions
      .withExternal()
        .source(OrderState.ORDERED).event(OrderEvent.ON_SHIPMENT)
.target(OrderState.SHIPPED) .and() .withExternal() .source(OrderState.ORDERED).event(OrderEvent.ON_CANCEL) .target(OrderState.CANCELLED) .and() ... } }
Örnek
Şöyle yaparız
@Configuration
@EnableStateMachineFactory
class PaymentStateMachineConfiguration :
  EnumStateMachineConfigurerAdapter<PaymentStates, PaymentEvents>() {

  override fun configure(states:
    StateMachineStateConfigurer<PaymentStates, PaymentEvents>) {
    states.withStates()
      .initial(AWAITING)
      .state(TRANSACTION_CREATED)
      .state(TRANSACTION_SENT)
      .state(TRANSACTION_FAILED)
      .end(SUCCEEDED)
  }

  override fun configure(transitions:
    StateMachineTransitionConfigurer<PaymentStates,PaymentEvents>) {
    transitions
      .withExternal()
      .source(AWAITING).target(TRANSACTION_CREATED).event(CREATE_TRANSACTION)
      .and()

      .withExternal()
      .source(TRANSACTION_CREATED).target(TRANSACTION_SENT).event(SEND_TRANSACTION)
      .and()
      ...
  }
}

SpringBoot StateMachine StateMachineListener Arayüzü

Giriş
Şu satırı dahil ederiz
import org.springframework.statemachine.listener.StateMachineListener;
İskeleti şöyle
public class OrderStateMachineListener extends StateMachineListener<OrderState, OrderEvent>{

 void stateChanged(State<OrderState, OrderEvent> var1, State<OrderState, OrderEvent> var2){}
void stateEntered(State<OrderState, OrderEvent> var1){}
void stateExited(State<OrderState, OrderEvent> var1){}
void eventNotAccepted(Message<OrderEvent> var1){}
void transition(Transition<OrderState, OrderEvent> var1){} void transitionStarted(Transition<OrderState, OrderEvent> var1){}
void transitionEnded(Transition<OrderState, OrderEvent> var1){}
void stateMachineStarted(StateMachine<OrderState, OrderEvent> var1){}
void stateMachineStopped(StateMachine<OrderState, OrderEvent> var1){}
void stateMachineError(State<OrderState, OrderEvent> var1, Exception var2){}
void extendedStateChanged(Object var1, Object var2){} void stateContext(StateContext<OrderState, OrderEvent> var1){}
}

SpringBoot StateMachine Guard Arayüzü

Giriş
Şu satırı dahil ederiz
import org.springframework.statemachine.Guard;
Kullanım
Örnek
Şöyle yaparız
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends 
  EnumStateMachineConfigurerAdapter<OrderState, OrderEvent> {

  @Override
  public void configure(
    StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions)
  throws Exception {
    transitions
      .withExternal()
        .source(OrderState.PICKUP).event(OrderEvent.INITIATE_REFUND)
        .target(OrderState.REFUND_INITIATED)
        .guard(new RefundInitiateGuard())
      .and()
      .withExternal()
      ...
  }
}
evaluate metodu
Örnek
Şöyle yaparız
public class RefundInitiateGuard implements Guard<OrderState,OrderEvent> {

  @Override
  public boolean evaluate(StateContext<OrderState, OrderEvent> stateContext) {
    ...
    return true;
  }
}

SpringBoot StateMachine StateMachine Arayüzü

Giriş
Şu satırı dahil ederiz
import org.springframework.statemachine.StateMachine;
sendEvent metodu
Örnek
Şöyle yaparız
public class StateMachineExample {

  StateMachine<OrderState,OrderEvent> stateMachine;
    
  public void onShipment(OrderDetails order){
    stateMachine.sendEvent(OrderEvent.ON_SHIPMENT);
  }
}


12 Ocak 2021 Salı

SpringBoot StateMachine Kullanımı

Giriş 
Şu satırı dahil ederiz
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.support.DefaultStateMachineContext;
Bu projeyi ilk olarak burada gördüm

Maven
Şu satırı dahil ederiz
<dependency>
   <groupId>org.springframework.statemachine</groupId>
   <artifactId>spring-statemachine-starter</artifactId>
</dependency>
1. Önce State'ler Enum olarak tanımlanır
2. Action'lar tanımlanır
3. Guard koşulları tanımlanır
3. EnumStateMachineConfigurerAdapter sınıfından kalıtarak Transition'lar, Guard'lar ve transition esnasında çalıştırılacak Action'lar takılır. Bu sınıfa @EnableStateMachineFactory anotasyonu eklenir.
4. StateMachineListener arayüzünden kalıtarak transition'lar esnasında bir şey çalıştırılabilir
5. StateMachine nesnesi Autowire edilir ve sendEvent() metodu kullanılarak event gönderilir.

Reactive StateMachine
Açıklaması şöyle
1. Spring State Machine version 3.0+ is fully reactive.

2. If the project is not based on the reactive stack, it makes sense to pay attention to version 2.5+, which is synchronous.

sendEvent method of the synchronous version returns true or false. But true is returned if the event is accepted, i.e. doesn’t violate the transition rules of the state machine. In this case, if an error occurred in the guard, action, or interceptor, then the method will still return true.

This is rather inconvenient if you want to include state transition in the current database transaction. To solve this problem, you can write wrappers that put the error into the state machine context.
Bu durumda StateMachineInterceptor kullanılır
Örnek
Şöyle yaparız
class StateMachineInterceptorWrapper<S, E>(
    private val interceptor: StateMachineInterceptor<S, E>,
) : StateMachineInterceptor<S, E> {

    ...
    
  override fun preEvent(message: Message<E>, stateMachine: StateMachine<S, E>): 
    Message<E> = wrap(stateMachine) {
    interceptor.preEvent(message, stateMachine)
  }
    
  private fun <T> wrap(stateMachine: StateMachine<S, E>, block: () -> T): T =
    try {
      block()
    } catch (t: Throwable) {
      stateMachine.extendedState.variables[STATE_MACHINE_ERROR_VARIABLE] = t
      stateMachine.setStateMachineError(RuntimeException(t))
      throw t
    }
}

fun <S, E> StateMachine<S, E>.sendEventOrThrow(event: E) {
  if (!sendEvent(event) || hasStateMachineError()) {
    val ex = extendedState.variables[STATE_MACHINE_ERROR_VARIABLE] as Throwable?
       ?: RuntimeException()
     throw ex
  }
}

Scheduled transitions
Açıklaması şöyle
SSM provides a simple mechanism for timed messages based on the ScheduledExecutorService
State persistence
Açıklaması şöyle
There are 2 ways to store a state.

1. You can use additional libraries such as Spring Statemachine Data Jpa to persist all state machines, their contexts, states' history, transitions, as well as guards and actions. Each entity will be stored in the corresponding database table.

2. If your project is already big enough, it is unlikely you want to refactor it significantly. Therefore, the second option is to keep persisting states as it is, but on each transition:

create an instance of the state machine
load the initial state
do the transition
update the state in the database

StateMachineFactory Sınıfı
Şu satırı dahil ederiz
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.support.DefaultStateMachineContext;
getStateMachine metodu
Örnek
Şöyle yaparız
PaymentStateChangeInterceptor paymentStateChangeInterceptor = ...;

StateMachine<PaymentState, PaymentEvent> build(Long paymentId){
  Payment payment = repository.getOne(paymentId);

  StateMachine<PaymentState, PaymentEvent> sm = stateMachineFactory
.getStateMachine(Long.toString(payment.getId()));

  sm.stop();

  sm.getStateMachineAccessor()
    .doWithAllRegions(sma -> {
      sma.addStateMachineInterceptor(paymentStateChangeInterceptor);
      sma.resetStateMachine(new DefaultStateMachineContext<>(payment.getState(),
null, null, null));
  });

  sm.start();

  return sm;
}
StateMachineInterceptorAdapter Sınıfı
preStateChange metodu
Örnek
Şöyle yaparız
import org.springframework.messaging.Message;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.support.StateMachineInterceptorAdapter;
import org.springframework.statemachine.transition.Transition;
import org.springframework.stereotype.Component;

@Component
public class PaymentStateChangeInterceptor extends
StateMachineInterceptorAdapter<PaymentState, PaymentEvent> { private final PaymentRepository paymentRepository; @Override public void preStateChange(State<PaymentState, PaymentEvent> state,
Message<PaymentEvent> message, Transition<PaymentState, PaymentEvent> transition,
StateMachine<PaymentState, PaymentEvent> stateMachine) { Optional.ofNullable(message).ifPresent(msg -> { Optional.ofNullable(Long.class.cast(msg.getHeaders()
.getOrDefault(PaymentServiceImpl.PAYMENT_ID_HEADER, -1L))) .ifPresent(paymentId -> { Payment payment = paymentRepository.getOne(paymentId); payment.setState(state.getId()); paymentRepository.save(payment); }); }); } }