Event publish and listener changes
This commit is contained in:
@@ -42,6 +42,6 @@ public interface StateMachine<S, E> extends Region<S, E> {
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
void addStateListener(StateMachineListener<State<S,E>, E> listener);
|
||||
void addStateListener(StateMachineListener<S, E> listener);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,10 +18,11 @@ package org.springframework.statemachine.event;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link StateMachineEventPublisher}.
|
||||
*
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@@ -30,20 +31,25 @@ public class DefaultStateMachineEventPublisher implements StateMachineEventPubli
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
/**
|
||||
* Instantiates a new leader event publisher.
|
||||
* Instantiates a new state machine event publisher.
|
||||
*/
|
||||
public DefaultStateMachineEventPublisher() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new leader event publisher.
|
||||
*
|
||||
* Instantiates a new state machine event publisher.
|
||||
*
|
||||
* @param applicationEventPublisher the application event publisher
|
||||
*/
|
||||
public DefaultStateMachineEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishStateChanged(Object source, State<?, ?> sourceState, State<?, ?> targetState) {
|
||||
if (applicationEventPublisher != null) {
|
||||
@@ -52,8 +58,24 @@ public class DefaultStateMachineEventPublisher implements StateMachineEventPubli
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
public void publishTransitionStart(Object source, Transition<?, ?> transition) {
|
||||
if (applicationEventPublisher != null) {
|
||||
applicationEventPublisher.publishEvent(new OnTransitionStartEvent(source, transition));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void publishTransitionEnd(Object source, Transition<?, ?> transition) {
|
||||
if (applicationEventPublisher != null) {
|
||||
applicationEventPublisher.publishEvent(new OnTransitionEndEvent(source, transition));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishTransition(Object source, Transition<?, ?> transition) {
|
||||
if (applicationEventPublisher != null) {
|
||||
applicationEventPublisher.publishEvent(new OnTransitionEvent(source, transition));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,13 +22,13 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Simple {@link ApplicationListener} which logs all events
|
||||
* based on {@link AbstractStateMachineEvent} using a log level
|
||||
* based on {@link StateMachineEvent} using a log level
|
||||
* set during the construction.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class LoggingListener implements ApplicationListener<AbstractStateMachineEvent> {
|
||||
public class LoggingListener implements ApplicationListener<StateMachineEvent> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(LoggingListener.class);
|
||||
|
||||
@@ -64,7 +64,7 @@ public class LoggingListener implements ApplicationListener<AbstractStateMachine
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(AbstractStateMachineEvent event) {
|
||||
public void onApplicationEvent(StateMachineEvent event) {
|
||||
switch (this.level) {
|
||||
case FATAL:
|
||||
if (log.isFatalEnabled()) {
|
||||
|
||||
@@ -24,14 +24,14 @@ import org.springframework.statemachine.state.State;
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class OnStateChangedEvent extends AbstractStateMachineEvent {
|
||||
public class OnStateChangedEvent extends StateMachineEvent {
|
||||
|
||||
private final State<?, ?> sourceState;
|
||||
private final State<?, ?> targetState;
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates a new granted event.
|
||||
*
|
||||
*
|
||||
* @param source the component that published the event (never {@code null})
|
||||
*/
|
||||
public OnStateChangedEvent(Object source, State<?, ?> sourceState, State<?, ?> targetState) {
|
||||
@@ -58,4 +58,9 @@ public class OnStateChangedEvent extends AbstractStateMachineEvent {
|
||||
return targetState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OnStateChangedEvent [sourceState=" + sourceState + ", targetState=" + targetState + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.event;
|
||||
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* Event representing that a {@link Transition} has ended.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class OnTransitionEndEvent extends TransitionEvent {
|
||||
|
||||
/**
|
||||
* Instantiates a new on transition end event.
|
||||
*
|
||||
* @param source the source
|
||||
* @param transition the transition
|
||||
*/
|
||||
public OnTransitionEndEvent(Object source, Transition<?, ?> transition) {
|
||||
super(source, transition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OnTransitionEndEvent [transition=" + getTransition() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.event;
|
||||
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* Event representing that a {@link Transition} has happened.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class OnTransitionEvent extends TransitionEvent {
|
||||
|
||||
/**
|
||||
* Instantiates a new on transition event.
|
||||
*
|
||||
* @param source the source
|
||||
* @param transition the transition
|
||||
*/
|
||||
public OnTransitionEvent(Object source, Transition<?, ?> transition) {
|
||||
super(source, transition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OnTransitionEvent [transition=" + getTransition() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.event;
|
||||
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* Event representing that a {@link Transition} has started.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class OnTransitionStartEvent extends TransitionEvent {
|
||||
|
||||
/**
|
||||
* Instantiates a new on transition start event.
|
||||
*
|
||||
* @param source the source
|
||||
* @param transition the transition
|
||||
*/
|
||||
public OnTransitionStartEvent(Object source, Transition<?, ?> transition) {
|
||||
super(source, transition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OnTransitionStartEvent [transition=" + getTransition() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,20 +25,20 @@ import org.springframework.context.ApplicationEvent;
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class AbstractStateMachineEvent extends ApplicationEvent {
|
||||
public abstract class StateMachineEvent extends ApplicationEvent {
|
||||
|
||||
/**
|
||||
* Create a new ApplicationEvent.
|
||||
*
|
||||
* @param source the component that published the event (never {@code null})
|
||||
*/
|
||||
public AbstractStateMachineEvent(Object source) {
|
||||
public StateMachineEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AbstractLeaderEvent [source=" + source + "]";
|
||||
return "AbstractStateMachineEvent [source=" + source + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.statemachine.event;
|
||||
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* Interface for publishing state machine based application events.
|
||||
@@ -33,5 +34,29 @@ public interface StateMachineEventPublisher {
|
||||
* @param targetState the target state
|
||||
*/
|
||||
void publishStateChanged(Object source, State<?, ?> sourceState, State<?, ?> targetState);
|
||||
|
||||
|
||||
/**
|
||||
* Publish a transition start event.
|
||||
*
|
||||
* @param source the source
|
||||
* @param transition the transition
|
||||
*/
|
||||
void publishTransitionStart(Object source, Transition<?, ?> transition);
|
||||
|
||||
/**
|
||||
* Publish a transition end event.
|
||||
*
|
||||
* @param source the source
|
||||
* @param transition the transition
|
||||
*/
|
||||
void publishTransitionEnd(Object source, Transition<?, ?> transition);
|
||||
|
||||
/**
|
||||
* Publish a transition event.
|
||||
*
|
||||
* @param source the source
|
||||
* @param transition the transition
|
||||
*/
|
||||
void publishTransition(Object source, Transition<?, ?> transition);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine.event;
|
||||
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* Generic base event representing something with a {@link Transition}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class TransitionEvent extends StateMachineEvent {
|
||||
|
||||
private final Transition<?, ?> transition;
|
||||
|
||||
/**
|
||||
* Instantiates a new transition event.
|
||||
*
|
||||
* @param source the source
|
||||
* @param transition the transition
|
||||
*/
|
||||
public TransitionEvent(Object source, Transition<?, ?> transition) {
|
||||
super(source);
|
||||
this.transition = transition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the transition.
|
||||
*
|
||||
* @return the transition
|
||||
*/
|
||||
public Transition<?, ?> getTransition() {
|
||||
return transition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OnTransitionStartEvent [transition=" + transition + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,16 +18,49 @@ package org.springframework.statemachine.listener;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
public class CompositeStateMachineListener<S,E> extends AbstractCompositeListener<StateMachineListener<State<S,E>, E>> implements
|
||||
StateMachineListener<State<S,E>, E> {
|
||||
/**
|
||||
* Default {@link StateMachineListener} dispatcher.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class CompositeStateMachineListener<S,E> extends AbstractCompositeListener<StateMachineListener<S,E>> implements
|
||||
StateMachineListener<S, E> {
|
||||
|
||||
@Override
|
||||
public void stateChanged(State<S, E> from, State<S, E> to) {
|
||||
for (Iterator<StateMachineListener<State<S,E>, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
|
||||
StateMachineListener<State<S,E>, E> listener = iterator.next();
|
||||
for (Iterator<StateMachineListener<S, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
|
||||
StateMachineListener<S, E> listener = iterator.next();
|
||||
listener.stateChanged(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transition(Transition<S, E> transition) {
|
||||
for (Iterator<StateMachineListener<S, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
|
||||
StateMachineListener<S, E> listener = iterator.next();
|
||||
listener.transition(transition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transitionStarted(Transition<S, E> transition) {
|
||||
for (Iterator<StateMachineListener<S, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
|
||||
StateMachineListener<S, E> listener = iterator.next();
|
||||
listener.transitionStarted(transition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transitionEnded(Transition<S, E> transition) {
|
||||
for (Iterator<StateMachineListener<S, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
|
||||
StateMachineListener<S, E> listener = iterator.next();
|
||||
listener.transitionEnded(transition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,8 +15,46 @@
|
||||
*/
|
||||
package org.springframework.statemachine.listener;
|
||||
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* {@code StateMachineListener} for various state machine events.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public interface StateMachineListener<S,E> {
|
||||
|
||||
void stateChanged(S from, S to);
|
||||
/**
|
||||
* Notified when state is changed.
|
||||
*
|
||||
* @param from the source state
|
||||
* @param to the target state
|
||||
*/
|
||||
void stateChanged(State<S,E> from, State<S,E> to);
|
||||
|
||||
/**
|
||||
* Notified when transition happened.
|
||||
*
|
||||
* @param transition the transition
|
||||
*/
|
||||
void transition(Transition<S, E> transition);
|
||||
|
||||
/**
|
||||
* Notified when transition started.
|
||||
*
|
||||
* @param transition the transition
|
||||
*/
|
||||
void transitionStarted(Transition<S, E> transition);
|
||||
|
||||
/**
|
||||
* Notified when transition ended.
|
||||
*
|
||||
* @param transition the transition
|
||||
*/
|
||||
void transitionEnded(Transition<S, E> transition);
|
||||
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
|
||||
@Override
|
||||
public boolean sendEvent(Message<E> event) {
|
||||
if (isComplete()) {
|
||||
if (isComplete() || !isRunning()) {
|
||||
return false;
|
||||
}
|
||||
// TODO: machine header looks weird!
|
||||
@@ -199,7 +199,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addStateListener(StateMachineListener<State<S, E>, E> listener) {
|
||||
public void addStateListener(StateMachineListener<S, E> listener) {
|
||||
stateListener.register(listener);
|
||||
}
|
||||
|
||||
@@ -308,10 +308,13 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
if (StateMachineUtils.containsAtleastOne(source.getIds(), currentState.getIds())) {
|
||||
if (trigger != null && trigger.evaluate(queuedEvent.getPayload())) {
|
||||
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(queuedEvent.getHeaders(), extendedState, transition);
|
||||
notifyTransitionStart(transition);
|
||||
boolean transit = transition.transit(stateContext);
|
||||
if (transit && transition.getKind() != TransitionKind.INTERNAL) {
|
||||
switchToState(target, queuedEvent, transition);
|
||||
notifyTransition(transition);
|
||||
}
|
||||
notifyTransitionEnd(transition);
|
||||
break;
|
||||
} else if (source.getDeferredEvents() != null && source.getDeferredEvents().contains(queuedEvent.getPayload())) {
|
||||
defer = queuedEvent;
|
||||
@@ -423,4 +426,28 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyTransitionStart(Transition<S,E> transition) {
|
||||
stateListener.transitionStarted(transition);
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishTransitionStart(this, transition);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyTransition(Transition<S,E> transition) {
|
||||
stateListener.transition(transition);
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishTransitionEnd(this, transition);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyTransitionEnd(Transition<S,E> transition) {
|
||||
stateListener.transitionEnded(transition);
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
eventPublisher.publishTransition(this, transition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -183,16 +183,18 @@ public class StateMachineEventTests extends AbstractStateMachineTests {
|
||||
|
||||
}
|
||||
|
||||
static class TestEventListener implements ApplicationListener<AbstractStateMachineEvent> {
|
||||
static class TestEventListener implements ApplicationListener<StateMachineEvent> {
|
||||
|
||||
CountDownLatch onEventLatch = new CountDownLatch(6);
|
||||
|
||||
ArrayList<AbstractStateMachineEvent> events = new ArrayList<AbstractStateMachineEvent>();
|
||||
ArrayList<StateMachineEvent> events = new ArrayList<StateMachineEvent>();
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(AbstractStateMachineEvent event) {
|
||||
events.add(event);
|
||||
onEventLatch.countDown();
|
||||
public void onApplicationEvent(StateMachineEvent event) {
|
||||
if (event instanceof OnStateChangedEvent) {
|
||||
events.add(event);
|
||||
onEventLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* Tests for state machine listener functionality.
|
||||
@@ -95,7 +96,7 @@ public class ListenerTests extends AbstractStateMachineTests {
|
||||
|
||||
}
|
||||
|
||||
private static class TestStateMachineListener implements StateMachineListener<State<TestStates, TestEvents>, TestEvents> {
|
||||
private static class TestStateMachineListener implements StateMachineListener<TestStates, TestEvents> {
|
||||
|
||||
ArrayList<Holder> states = new ArrayList<Holder>();
|
||||
|
||||
@@ -113,6 +114,18 @@ public class ListenerTests extends AbstractStateMachineTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transition(Transition<TestStates, TestEvents> transition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transitionStarted(Transition<TestStates, TestEvents> transition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transitionEnded(Transition<TestStates, TestEvents> transition) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
Reference in New Issue
Block a user