initial import
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.support.AbstractStateMachine;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* Specialisation of a {@link StateMachine} using enums
|
||||
* as its {@link State} and event types.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class EnumStateMachine<S extends Enum<S>, E extends Enum<E>> extends AbstractStateMachine<S, E> {
|
||||
|
||||
/**
|
||||
* Instantiates a new enum state machine.
|
||||
*
|
||||
* @param states the states
|
||||
* @param transitions the transitions
|
||||
* @param initialState the initial state
|
||||
*/
|
||||
public EnumStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
|
||||
State<S, E> initialState) {
|
||||
super(states, transitions, initialState);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Extended states are used to supplement state machine with a variables. If
|
||||
* extended state is used a complete condition of a state machine is a
|
||||
* combination of its state an extended state variables.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public interface ExtendedState {
|
||||
|
||||
/**
|
||||
* Gets the extended state variables.
|
||||
*
|
||||
* @return the extended state variables
|
||||
*/
|
||||
Map<String, Object> getVariables();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* {@code StateContext} is representing a current context used in
|
||||
* {@link Transition}s, {@link Action}s and {@link Guard}s order to get access
|
||||
* to event headers and {@link ExtendedState}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public interface StateContext {
|
||||
|
||||
/**
|
||||
* Gets the event message headers.
|
||||
*
|
||||
* @return the event message headers
|
||||
*/
|
||||
MessageHeaders getMessageHeaders();
|
||||
|
||||
/**
|
||||
* Gets the state machine extended state.
|
||||
*
|
||||
* @return the state machine extended state
|
||||
*/
|
||||
ExtendedState getExtendedState();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.listener.StateMachineListener;
|
||||
|
||||
/**
|
||||
* {@code StateMachine} provides an APIs for generic finite state machine needed
|
||||
* for basic operations like working with states, events and a lifecycle.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public interface StateMachine<S, E> {
|
||||
|
||||
/**
|
||||
* Gets the initial state {@code S}.
|
||||
*
|
||||
* @return initial state
|
||||
*/
|
||||
S getInitialState();
|
||||
|
||||
/**
|
||||
* Gets the current state {@code S}.
|
||||
*
|
||||
* @return current state
|
||||
*/
|
||||
S getState();
|
||||
|
||||
/**
|
||||
* Start the state machine.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Send an event {@code E} wrapped with a {@link Message} to the state
|
||||
* machine.
|
||||
*
|
||||
* @param event the wrapped event to send
|
||||
*/
|
||||
void sendEvent(Message<E> event);
|
||||
|
||||
/**
|
||||
* Send an event {@code E} to the state machine.
|
||||
*
|
||||
* @param event the event to send
|
||||
*/
|
||||
void sendEvent(E event);
|
||||
|
||||
/**
|
||||
* Adds the state listener.
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
void addStateListener(StateMachineListener<S, E> listener);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Various constants used in state machine lib.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public abstract class StateMachineSystemConstants {
|
||||
|
||||
/** Default bean id for state machine. */
|
||||
public static final String DEFAULT_ID_STATEMACHINE = "stateMachine";
|
||||
|
||||
/** Default bean id for state machine factory. */
|
||||
public static final String DEFAULT_ID_STATEMACHINEFACTORY = "stateMachineFactory";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.action;
|
||||
|
||||
import org.springframework.statemachine.StateContext;
|
||||
|
||||
/**
|
||||
* Generic strategy interface used by a state machine to respond
|
||||
* events by executing an {@code Action} with a {@link StateContext}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public interface Action {
|
||||
|
||||
/**
|
||||
* Execute action with a {@link StateContext}.
|
||||
*
|
||||
* @param context the state context
|
||||
*/
|
||||
void execute(StateContext context);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface OnTransition {
|
||||
|
||||
String source() default "";
|
||||
|
||||
String target() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Component
|
||||
public @interface WithStateMachine {
|
||||
|
||||
String name() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.statemachine.StateMachineSystemConstants;
|
||||
import org.springframework.statemachine.config.common.annotation.EnableAnnotationConfiguration;
|
||||
import org.springframework.statemachine.config.common.annotation.configuration.ObjectPostProcessorConfiguration;
|
||||
import org.springframework.statemachine.config.configuration.StateMachineConfiguration;
|
||||
|
||||
/**
|
||||
* Example annotation which imports @{@link Configuration}s.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@EnableAnnotationConfiguration
|
||||
@Import({StateMachineConfiguration.class,ObjectPostProcessorConfiguration.class})
|
||||
public @interface EnableStateMachine {
|
||||
|
||||
/**
|
||||
* The name of bean, or if plural, aliases for bean created based on this
|
||||
* annotation. If left unspecified bean name will be autogenerated.
|
||||
*
|
||||
* @see Bean#name()
|
||||
* @return the array if names or empty as default
|
||||
*/
|
||||
String[] name() default {StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.statemachine.StateMachineSystemConstants;
|
||||
import org.springframework.statemachine.config.common.annotation.EnableAnnotationConfiguration;
|
||||
import org.springframework.statemachine.config.common.annotation.configuration.ObjectPostProcessorConfiguration;
|
||||
import org.springframework.statemachine.config.configuration.StateMachineFactoryConfiguration;
|
||||
|
||||
/**
|
||||
* Example annotation which imports @{@link Configuration}s.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@EnableAnnotationConfiguration
|
||||
@Import({StateMachineFactoryConfiguration.class,ObjectPostProcessorConfiguration.class})
|
||||
public @interface EnableStateMachineFactory {
|
||||
|
||||
/**
|
||||
* The name of bean, or if plural, aliases for bean created based on this
|
||||
* annotation. If left unspecified bean name will be autogenerated.
|
||||
*
|
||||
* @see Bean#name()
|
||||
* @return the array if names or empty as default
|
||||
*/
|
||||
String[] name() default {StateMachineSystemConstants.DEFAULT_ID_STATEMACHINEFACTORY};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
public class EnumStateMachineConfigurerAdapter<S extends Enum<S>, E extends Enum<E>> extends StateMachineConfigurerAdapter<S, E> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.statemachine.EnumStateMachine;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStates;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitions;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStates.StateData;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitions.TransitionData;
|
||||
import org.springframework.statemachine.state.EnumState;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.support.LifecycleObjectSupport;
|
||||
import org.springframework.statemachine.transition.DefaultExternalTransition;
|
||||
import org.springframework.statemachine.transition.DefaultInternalTransition;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
import org.springframework.statemachine.transition.TransitionKind;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> extends LifecycleObjectSupport implements
|
||||
StateMachineFactory<State<S, E>, E> {
|
||||
|
||||
private final StateMachineTransitions<S, E> stateMachineTransitions;
|
||||
|
||||
private final StateMachineStates<S, E> stateMachineStates;
|
||||
|
||||
/**
|
||||
* Instantiates a new enum state machine factory.
|
||||
*
|
||||
* @param stateMachineTransitions the state machine transitions
|
||||
* @param stateMachineStates the state machine states
|
||||
*/
|
||||
public EnumStateMachineFactory(StateMachineTransitions<S, E> stateMachineTransitions,
|
||||
StateMachineStates<S, E> stateMachineStates) {
|
||||
this.stateMachineTransitions = stateMachineTransitions;
|
||||
this.stateMachineStates = stateMachineStates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateMachine<State<S, E>, E> getStateMachine() {
|
||||
return stateMachine();
|
||||
}
|
||||
|
||||
public StateMachine<State<S, E>, E> stateMachine() {
|
||||
Map<S, State<S, E>> stateMap = new HashMap<S, State<S, E>>();
|
||||
for (StateData<S, E> stateData : stateMachineStates.getStates()) {
|
||||
stateMap.put(stateData.getState(), new EnumState<S, E>(stateData.getState(), stateData.getDeferred(),
|
||||
stateData.getEntryActions(), stateData.getExitActions()));
|
||||
}
|
||||
|
||||
Collection<Transition<S, E>> transitions = new ArrayList<Transition<S, E>>();
|
||||
for (TransitionData<S, E> transitionData : stateMachineTransitions.getTransitions()) {
|
||||
S source = transitionData.getSource();
|
||||
S target = transitionData.getTarget();
|
||||
E event = transitionData.getEvent();
|
||||
if (transitionData.getKind() == TransitionKind.EXTERNAL) {
|
||||
DefaultExternalTransition<S, E> transition = new DefaultExternalTransition<S, E>(stateMap.get(source),
|
||||
stateMap.get(target), transitionData.getActions(), event, transitionData.getGuard());
|
||||
transitions.add(transition);
|
||||
|
||||
} else if (transitionData.getKind() == TransitionKind.INTERNAL) {
|
||||
DefaultInternalTransition<S, E> transition = new DefaultInternalTransition<S, E>(stateMap.get(source),
|
||||
transitionData.getActions(), event, transitionData.getGuard());
|
||||
transitions.add(transition);
|
||||
}
|
||||
}
|
||||
|
||||
EnumStateMachine<S, E> machine = new EnumStateMachine<S, E>(stateMap.values(), transitions,
|
||||
stateMap.get(stateMachineStates.getInitialState()));
|
||||
machine.afterPropertiesSet();
|
||||
if (getBeanFactory() != null) {
|
||||
machine.setBeanFactory(getBeanFactory());
|
||||
}
|
||||
machine.setAutoStartup(isAutoStartup());
|
||||
machine.start();
|
||||
return machine;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import org.springframework.statemachine.config.builders.StateMachineStates;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitions;
|
||||
|
||||
public class StateMachineConfig<S, E> {
|
||||
|
||||
public final StateMachineTransitions<S, E> transitions;
|
||||
|
||||
public final StateMachineStates<S, E> states;
|
||||
|
||||
public StateMachineConfig(StateMachineTransitions<S, E> transitions, StateMachineStates<S, E> states) {
|
||||
this.transitions = transitions;
|
||||
this.states = states;
|
||||
}
|
||||
|
||||
public StateMachineTransitions<S, E> getTransitions() {
|
||||
return transitions;
|
||||
}
|
||||
|
||||
public StateMachineStates<S, E> getStates() {
|
||||
return states;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import org.springframework.statemachine.config.builders.StateMachineConfigBuilder;
|
||||
import org.springframework.statemachine.config.builders.StateMachineConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateBuilder;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionBuilder;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationBuilder;
|
||||
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
|
||||
|
||||
public class StateMachineConfigurerAdapter<S, E> implements StateMachineConfigurer<S, E> {
|
||||
|
||||
private StateMachineTransitionBuilder<S, E> transitionBuilder;
|
||||
private StateMachineStateBuilder<S, E> stateBuilder;
|
||||
|
||||
@Override
|
||||
public final void init(StateMachineConfigBuilder<S, E> config) throws Exception {
|
||||
config.setSharedObject(StateMachineTransitionBuilder.class, getStateMachineTransitionBuilder());
|
||||
config.setSharedObject(StateMachineStateBuilder.class, getStateMachineStateBuilder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineConfigBuilder<S, E> config) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<S, E> states) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<S, E> transitions) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssignable(AnnotationBuilder<StateMachineConfig<S, E>> builder) {
|
||||
return builder instanceof StateMachineConfigBuilder;
|
||||
}
|
||||
|
||||
protected final StateMachineTransitionBuilder<S, E> getStateMachineTransitionBuilder() throws Exception {
|
||||
if (transitionBuilder != null) {
|
||||
return transitionBuilder;
|
||||
}
|
||||
transitionBuilder = new StateMachineTransitionBuilder<S, E>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true);
|
||||
configure(transitionBuilder);
|
||||
return transitionBuilder;
|
||||
}
|
||||
|
||||
protected final StateMachineStateBuilder<S, E> getStateMachineStateBuilder() throws Exception {
|
||||
if (stateBuilder != null) {
|
||||
return stateBuilder;
|
||||
}
|
||||
stateBuilder = new StateMachineStateBuilder<S, E>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true);
|
||||
configure(stateBuilder);
|
||||
return stateBuilder;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
|
||||
public interface StateMachineFactory<S, E> {
|
||||
|
||||
StateMachine<S, E> getStateMachine();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.config.builders;
|
||||
|
||||
import org.springframework.statemachine.config.StateMachineConfig;
|
||||
import org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder;
|
||||
|
||||
public class StateMachineConfigBuilder<S, E>
|
||||
extends
|
||||
AbstractConfiguredAnnotationBuilder<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>, StateMachineConfigBuilder<S, E>> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected StateMachineConfig<S, E> performBuild() throws Exception {
|
||||
StateMachineTransitionBuilder<?, ?> sharedObject = getSharedObject(StateMachineTransitionBuilder.class);
|
||||
StateMachineStateBuilder<?, ?> sharedObject2 = getSharedObject(StateMachineStateBuilder.class);
|
||||
StateMachineStates<S, E> states = (StateMachineStates<S, E>) sharedObject2.build();
|
||||
StateMachineTransitions<S, E> transitions = (StateMachineTransitions<S, E>) sharedObject.build();
|
||||
StateMachineConfig<S, E> bean = new StateMachineConfig<S, E>(transitions, states);
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.config.builders;
|
||||
|
||||
import org.springframework.statemachine.config.StateMachineConfig;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurer;
|
||||
|
||||
public interface StateMachineConfigurer<S, E> extends
|
||||
AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>> {
|
||||
|
||||
void configure(StateMachineStateConfigurer<S, E> transitions) throws Exception;
|
||||
|
||||
void configure(StateMachineTransitionConfigurer<S, E> transitions) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.config.builders;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.config.builders.StateMachineStates.StateData;
|
||||
import org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder;
|
||||
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
|
||||
import org.springframework.statemachine.config.configurers.DefaultStateConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.StateConfigurer;
|
||||
|
||||
public class StateMachineStateBuilder<S, E>
|
||||
extends AbstractConfiguredAnnotationBuilder<StateMachineStates<S, E>, StateMachineStateConfigurer<S, E>, StateMachineStateBuilder<S, E>>
|
||||
implements StateMachineStateConfigurer<S, E> {
|
||||
|
||||
private Collection<StateData<S, E>> states = new ArrayList<StateData<S, E>>();
|
||||
private S initialState;
|
||||
|
||||
public StateMachineStateBuilder() {
|
||||
super();
|
||||
}
|
||||
|
||||
public StateMachineStateBuilder(ObjectPostProcessor<Object> objectPostProcessor,
|
||||
boolean allowConfigurersOfSameType) {
|
||||
super(objectPostProcessor, allowConfigurersOfSameType);
|
||||
}
|
||||
|
||||
public StateMachineStateBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
super(objectPostProcessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StateMachineStates<S, E> performBuild() throws Exception {
|
||||
StateMachineStates<S, E> bean = new StateMachineStates<S, E>(initialState, states);
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> withStates() throws Exception {
|
||||
return apply(new DefaultStateConfigurer<S, E>());
|
||||
}
|
||||
|
||||
public void add(Collection<StateData<S, E>> states) {
|
||||
this.states.addAll(states);
|
||||
}
|
||||
|
||||
public void setInitialState(S state) {
|
||||
this.initialState = state;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.config.builders;
|
||||
|
||||
import org.springframework.statemachine.config.configurers.StateConfigurer;
|
||||
|
||||
public interface StateMachineStateConfigurer<S, E> {
|
||||
|
||||
StateConfigurer<S, E> withStates() throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.config.builders;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
|
||||
public class StateMachineStates<S, E> {
|
||||
|
||||
private Collection<StateData<S, E>> states;
|
||||
|
||||
private final S initialState;
|
||||
|
||||
public StateMachineStates(S initialState, Collection<StateData<S, E>> states) {
|
||||
this.states = states;
|
||||
this.initialState = initialState;
|
||||
}
|
||||
|
||||
public Collection<StateData<S, E>> getStates() {
|
||||
return states;
|
||||
}
|
||||
|
||||
public S getInitialState() {
|
||||
return initialState;
|
||||
}
|
||||
|
||||
public static class StateData<S, E> {
|
||||
private S state;
|
||||
private Collection<E> deferred;
|
||||
private Collection<Action> entryActions;
|
||||
private Collection<Action> exitActions;
|
||||
public StateData(S state, Collection<E> deferred) {
|
||||
this(state, deferred, null, null);
|
||||
}
|
||||
public StateData(S state, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions) {
|
||||
this.state = state;
|
||||
this.deferred = deferred;
|
||||
this.entryActions = entryActions;
|
||||
this.exitActions = exitActions;
|
||||
}
|
||||
public StateData(S state, E[] deferred) {
|
||||
this(state, deferred != null ? Arrays.asList(deferred) : new ArrayList<E>());
|
||||
}
|
||||
public S getState() {
|
||||
return state;
|
||||
}
|
||||
public Collection<E> getDeferred() {
|
||||
return deferred;
|
||||
}
|
||||
public Collection<Action> getEntryActions() {
|
||||
return entryActions;
|
||||
}
|
||||
public Collection<Action> getExitActions() {
|
||||
return exitActions;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.config.builders;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitions.TransitionData;
|
||||
import org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder;
|
||||
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
|
||||
import org.springframework.statemachine.config.configurers.DefaultExternalTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.DefaultInternalTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.ExternalTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.InternalTransitionConfigurer;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.transition.TransitionKind;
|
||||
|
||||
public class StateMachineTransitionBuilder<S, E>
|
||||
extends
|
||||
AbstractConfiguredAnnotationBuilder<StateMachineTransitions<S, E>, StateMachineTransitionConfigurer<S, E>, StateMachineTransitionBuilder<S, E>>
|
||||
implements StateMachineTransitionConfigurer<S, E> {
|
||||
|
||||
private Collection<TransitionData<S, E>> transitionData = new ArrayList<TransitionData<S, E>>();
|
||||
|
||||
public StateMachineTransitionBuilder() {
|
||||
super();
|
||||
}
|
||||
|
||||
public StateMachineTransitionBuilder(ObjectPostProcessor<Object> objectPostProcessor,
|
||||
boolean allowConfigurersOfSameType) {
|
||||
super(objectPostProcessor, allowConfigurersOfSameType);
|
||||
}
|
||||
|
||||
public StateMachineTransitionBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
super(objectPostProcessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StateMachineTransitions<S, E> performBuild() throws Exception {
|
||||
StateMachineTransitions<S, E> bean = new StateMachineTransitions<S, E>(transitionData);
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternalTransitionConfigurer<S, E> withExternal() throws Exception {
|
||||
return apply(new DefaultExternalTransitionConfigurer<S, E>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTransitionConfigurer<S, E> withInternal() throws Exception {
|
||||
return apply(new DefaultInternalTransitionConfigurer<S, E>());
|
||||
}
|
||||
|
||||
public void add(S source, S target, E event, Collection<Action> actions, Guard guard, TransitionKind kind) {
|
||||
transitionData.add(new TransitionData<S, E>(source, target, event, actions, guard, kind));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.config.builders;
|
||||
|
||||
import org.springframework.statemachine.config.configurers.ExternalTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.configurers.InternalTransitionConfigurer;
|
||||
|
||||
public interface StateMachineTransitionConfigurer<S, E> {
|
||||
|
||||
ExternalTransitionConfigurer<S, E> withExternal() throws Exception;
|
||||
|
||||
InternalTransitionConfigurer<S, E> withInternal() throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.config.builders;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.transition.TransitionKind;
|
||||
|
||||
public class StateMachineTransitions<S, E> {
|
||||
|
||||
private Collection<TransitionData<S, E>> transitions;
|
||||
|
||||
public StateMachineTransitions(Collection<TransitionData<S, E>> transitions) {
|
||||
this.transitions = transitions;
|
||||
}
|
||||
|
||||
public Collection<TransitionData<S, E>> getTransitions() {
|
||||
return transitions;
|
||||
}
|
||||
|
||||
public static class TransitionData<S, E> {
|
||||
S source;
|
||||
S target;
|
||||
E event;
|
||||
Collection<Action> actions;
|
||||
Guard guard;
|
||||
TransitionKind kind;
|
||||
public TransitionData(S source, S target, E event, Collection<Action> actions, Guard guard, TransitionKind kind) {
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
this.event = event;
|
||||
this.actions = actions;
|
||||
this.guard = guard;
|
||||
this.kind = kind;
|
||||
}
|
||||
public S getSource() {
|
||||
return source;
|
||||
}
|
||||
public S getTarget() {
|
||||
return target;
|
||||
}
|
||||
public E getEvent() {
|
||||
return event;
|
||||
}
|
||||
public Collection<Action> getActions() {
|
||||
return actions;
|
||||
}
|
||||
public Guard getGuard() {
|
||||
return guard;
|
||||
}
|
||||
public TransitionKind getKind() {
|
||||
return kind;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.config.common.annotation;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* A base {@link AnnotationBuilder} that ensures the object being built is only
|
||||
* built one time.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <O> the type of Object that is being built
|
||||
*/
|
||||
public abstract class AbstractAnnotationBuilder<O> implements AnnotationBuilder<O> {
|
||||
|
||||
/** Flag tracking build */
|
||||
private AtomicBoolean building = new AtomicBoolean();
|
||||
|
||||
/** Built object is stored here */
|
||||
private O object;
|
||||
|
||||
@Override
|
||||
public final O build() throws Exception {
|
||||
if(building.compareAndSet(false, true)) {
|
||||
object = doBuild();
|
||||
return object;
|
||||
}
|
||||
throw new IllegalStateException("This object has already been built");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object that was built. If it has not been built yet an Exception
|
||||
* is thrown.
|
||||
*
|
||||
* @return the Object that was built
|
||||
*/
|
||||
public final O getObject() {
|
||||
if(!building.get()) {
|
||||
throw new IllegalStateException("This object has not been built");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should implement this to perform the build.
|
||||
*
|
||||
* @return the object that should be returned by {@link #build()}.
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
protected abstract O doBuild() throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.config.common.annotation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Base implementation of @{@link Configuration} class.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <O> The object that used builder returns
|
||||
* @param <B> The type of the builder
|
||||
*/
|
||||
public abstract class AbstractAnnotationConfiguration<B extends AnnotationBuilder<O>, O>
|
||||
implements ImportAware, BeanClassLoaderAware {
|
||||
|
||||
private List<AnnotationConfigurer<O,B>> configurers;
|
||||
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
private AnnotationAttributes annotationAttributes;
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
Map<String, Object> enableConfigurationAttrMap =
|
||||
importMetadata.getAnnotationAttributes(EnableAnnotationConfiguration.class.getName());
|
||||
AnnotationAttributes enableConfigurationAttrs = AnnotationAttributes.fromMap(enableConfigurationAttrMap);
|
||||
if(enableConfigurationAttrs == null) {
|
||||
// search parent classes
|
||||
Class<?> currentClass = ClassUtils.resolveClassName(importMetadata.getClassName(), beanClassLoader);
|
||||
for(Class<?> classToInspect = currentClass ;classToInspect != null; classToInspect = classToInspect.getSuperclass()) {
|
||||
EnableAnnotationConfiguration enableConfigurationAnnotation =
|
||||
AnnotationUtils.findAnnotation(classToInspect, EnableAnnotationConfiguration.class);
|
||||
if(enableConfigurationAnnotation == null) {
|
||||
continue;
|
||||
}
|
||||
enableConfigurationAttrMap = AnnotationUtils
|
||||
.getAnnotationAttributes(enableConfigurationAnnotation);
|
||||
enableConfigurationAttrs = AnnotationAttributes.fromMap(enableConfigurationAttrMap);
|
||||
}
|
||||
}
|
||||
annotationAttributes = enableConfigurationAttrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configurers.
|
||||
*
|
||||
* @param configurers the configurers
|
||||
* @throws Exception the exception
|
||||
*/
|
||||
@Autowired(required=false)
|
||||
public void setConfigurers(List<AnnotationConfigurer<O, B>> configurers) throws Exception {
|
||||
this.configurers = configurers;
|
||||
onConfigurers(configurers);
|
||||
}
|
||||
|
||||
public AnnotationAttributes getAnnotationAttributes() {
|
||||
return annotationAttributes;
|
||||
}
|
||||
|
||||
public List<AnnotationConfigurer<O, B>> getConfigurers() {
|
||||
return configurers;
|
||||
}
|
||||
|
||||
protected abstract void onConfigurers(List<AnnotationConfigurer<O, B>> configurers) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
* 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.config.common.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A base {@link AnnotationBuilder} that allows {@link AnnotationConfigurer}s to be
|
||||
* applied to it. This makes modifying the {@link AnnotationBuilder} a strategy
|
||||
* that can be customised and broken up into a number of
|
||||
* {@link AnnotationConfigurer} objects that have more specific goals than that
|
||||
* of the {@link AnnotationBuilder}.
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <O> The object that this builder returns
|
||||
* @param <I> The interface of type B
|
||||
* @param <B> The type of this builder (that is returned by the base class)
|
||||
*/
|
||||
public abstract class AbstractConfiguredAnnotationBuilder<O,I,B extends AnnotationBuilder<O>>
|
||||
extends AbstractAnnotationBuilder<O> {
|
||||
|
||||
private final static Log log = LogFactory.getLog(AbstractConfiguredAnnotationBuilder.class);
|
||||
|
||||
/** Configurers which are added to this builder before the configure step */
|
||||
private final LinkedHashMap<Class<? extends AnnotationConfigurer<O, B>>, List<AnnotationConfigurer<O, B>>> mainConfigurers =
|
||||
new LinkedHashMap<Class<? extends AnnotationConfigurer<O, B>>, List<AnnotationConfigurer<O, B>>>();
|
||||
|
||||
/** Configurers which are added to this builder during the configuration phase */
|
||||
private final LinkedHashMap<Class<? extends AnnotationConfigurer<O, B>>, List<AnnotationConfigurer<O, B>>> postConfigurers =
|
||||
new LinkedHashMap<Class<? extends AnnotationConfigurer<O, B>>, List<AnnotationConfigurer<O, B>>>();
|
||||
|
||||
private final Map<Class<Object>, Object> sharedObjects = new HashMap<Class<Object>, Object>();
|
||||
|
||||
private final boolean allowConfigurersOfSameType;
|
||||
|
||||
/** Current state of this builder */
|
||||
private BuildState buildState = BuildState.UNBUILT;
|
||||
|
||||
private ObjectPostProcessor<Object> objectPostProcessor;
|
||||
|
||||
/**
|
||||
* Instantiates a new annotation builder.
|
||||
*/
|
||||
protected AbstractConfiguredAnnotationBuilder() {
|
||||
this(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new annotation builder.
|
||||
*
|
||||
* @param objectPostProcessor the object post processor
|
||||
*/
|
||||
protected AbstractConfiguredAnnotationBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
this(objectPostProcessor,false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new annotation builder.
|
||||
*
|
||||
* @param objectPostProcessor the object post processor
|
||||
* @param allowConfigurersOfSameType the allow configurers of same type
|
||||
*/
|
||||
protected AbstractConfiguredAnnotationBuilder(ObjectPostProcessor<Object> objectPostProcessor, boolean allowConfigurersOfSameType) {
|
||||
Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
|
||||
this.objectPostProcessor = objectPostProcessor;
|
||||
this.allowConfigurersOfSameType = allowConfigurersOfSameType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final O doBuild() throws Exception {
|
||||
synchronized (mainConfigurers) {
|
||||
buildState = BuildState.INITIALIZING_MAINS;
|
||||
beforeInit();
|
||||
initMainConfigurers();
|
||||
|
||||
buildState = BuildState.CONFIGURING_MAINS;
|
||||
beforeConfigureMains();
|
||||
configureMainConfigurers();
|
||||
|
||||
buildState = BuildState.CONFIGURING_POSTS;
|
||||
beforeConfigurePosts();
|
||||
configurePostConfigurers();
|
||||
|
||||
buildState = BuildState.BUILDING;
|
||||
O result = performBuild();
|
||||
|
||||
buildState = BuildState.BUILT;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #build()} and {@link #getObject()} but checks the state
|
||||
* to determine if {@link #build()} needs to be called first.
|
||||
*
|
||||
* @return the result of {@link #build()} or {@link #getObject()}. If an
|
||||
* error occurs while building, returns null.
|
||||
*/
|
||||
public O getOrBuild() {
|
||||
if (isUnbuilt()) {
|
||||
try {
|
||||
return build();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to perform build. Returning null", e);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return getObject();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a {@link AnnotationConfigurerAdapter} to this
|
||||
* {@link AnnotationBuilder} and invokes
|
||||
* {@link AnnotationConfigurerAdapter#setBuilder(AnnotationBuilder)}.
|
||||
*
|
||||
* @param configurer the configurer
|
||||
* @param <C> type of AnnotationConfigurer
|
||||
* @return Configurer passed as parameter
|
||||
* @throws Exception if error occurred
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends AnnotationConfigurerAdapter<O,I,B>> C apply(C configurer) throws Exception {
|
||||
add(configurer);
|
||||
configurer.addObjectPostProcessor(objectPostProcessor);
|
||||
configurer.setBuilder((B) this);
|
||||
return configurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #apply(AnnotationConfigurer)} but checks the state
|
||||
* to determine if {@link #apply(AnnotationConfigurer)} needs to be called first.
|
||||
*
|
||||
* @param configurer the configurer
|
||||
* @param <C> type of AnnotationConfigurer
|
||||
* @return Configurer passed as parameter
|
||||
* @throws Exception if error occurred
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends AnnotationConfigurerAdapter<O,I,B>> C getOrApply(C configurer) throws Exception {
|
||||
C existing = (C) getConfigurer(configurer.getClass());
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
return apply(configurer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a {@link AnnotationConfigurer} to this {@link AnnotationBuilder}
|
||||
* overriding any {@link AnnotationConfigurer} of the exact same class. Note
|
||||
* that object hierarchies are not considered.
|
||||
*
|
||||
* @param configurer the configurer
|
||||
* @param <C> type of AnnotationConfigurer
|
||||
* @return Configurer passed as parameter
|
||||
* @throws Exception if error occurred
|
||||
*/
|
||||
public <C extends AnnotationConfigurer<O, B>> C apply(C configurer) throws Exception {
|
||||
add(configurer);
|
||||
return configurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an object that is shared by multiple {@link AnnotationConfigurer}.
|
||||
*
|
||||
* @param sharedType the Class to key the shared object by.
|
||||
* @param object the Object to store
|
||||
* @param <C> type of share object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C> void setSharedObject(Class<C> sharedType, C object) {
|
||||
this.sharedObjects.put((Class<Object>) sharedType, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a shared Object. Note that object hierarchies are not considered.
|
||||
*
|
||||
* @param sharedType the type of the shared Object
|
||||
* @param <C> type of share object
|
||||
* @return the shared Object or null if it is not found
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C> C getSharedObject(Class<C> sharedType) {
|
||||
return (C) this.sharedObjects.get(sharedType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shared objects.
|
||||
*
|
||||
* @return Shared objects
|
||||
*/
|
||||
public Map<Class<Object>, Object> getSharedObjects() {
|
||||
return Collections.unmodifiableMap(this.sharedObjects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds {@link AnnotationConfigurer} ensuring that it is allowed and
|
||||
* invoking {@link AnnotationConfigurer#init(AnnotationBuilder)} immediately
|
||||
* if necessary.
|
||||
*
|
||||
* @param configurer the {@link AnnotationConfigurer} to add
|
||||
* @param <C> type of AnnotationConfigurer
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <C extends AnnotationConfigurer<O, B>> void add(C configurer) throws Exception {
|
||||
Assert.notNull(configurer, "configurer cannot be null");
|
||||
|
||||
Class<? extends AnnotationConfigurer<O, B>> clazz =
|
||||
(Class<? extends AnnotationConfigurer<O, B>>) configurer.getClass();
|
||||
|
||||
if (!buildState.isConfigured()) {
|
||||
synchronized (mainConfigurers) {
|
||||
List<AnnotationConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.mainConfigurers.get(clazz) : null;
|
||||
if (configs == null) {
|
||||
configs = new ArrayList<AnnotationConfigurer<O, B>>(1);
|
||||
}
|
||||
configs.add(configurer);
|
||||
this.mainConfigurers.put(clazz, configs);
|
||||
if (buildState.isInitializing()) {
|
||||
configurer.init((B) this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
synchronized (postConfigurers) {
|
||||
List<AnnotationConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.postConfigurers.get(clazz) : null;
|
||||
if (configs == null) {
|
||||
configs = new ArrayList<AnnotationConfigurer<O, B>>(1);
|
||||
}
|
||||
configs.add(configurer);
|
||||
this.postConfigurers.put(clazz, configs);
|
||||
configurer.init((B) this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the {@link AnnotationConfigurer} instances by its class name or an
|
||||
* empty List if not found. Note that object hierarchies are not considered.
|
||||
*
|
||||
* @param clazz the {@link AnnotationConfigurer} class to look for
|
||||
* @param <C> type of AnnotationConfigurer
|
||||
* @return All configurers
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends AnnotationConfigurer<O, B>> List<C> getConfigurers(Class<C> clazz) {
|
||||
List<C> configs = (List<C>) this.mainConfigurers.get(clazz);
|
||||
if (configs == null) {
|
||||
return new ArrayList<C>();
|
||||
}
|
||||
return new ArrayList<C>(configs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all the {@link AnnotationConfigurer} instances by its class name or an
|
||||
* empty List if not found. Note that object hierarchies are not considered.
|
||||
*
|
||||
* @param clazz the {@link AnnotationConfigurer} class to look for
|
||||
* @param <C> type of AnnotationConfigurer
|
||||
* @return Empty list of configurers
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends AnnotationConfigurer<O, B>> List<C> removeConfigurers(Class<C> clazz) {
|
||||
List<C> configs = (List<C>) this.mainConfigurers.remove(clazz);
|
||||
if (configs == null) {
|
||||
return new ArrayList<C>();
|
||||
}
|
||||
return new ArrayList<C>(configs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link AnnotationConfigurer} by its class name or
|
||||
* <code>null</code> if not found. Note that object hierarchies are not
|
||||
* considered.
|
||||
*
|
||||
* @param clazz the configurer class type
|
||||
* @param <C> type of AnnotationConfigurer
|
||||
* @return Matched configurers
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends AnnotationConfigurer<O, B>> C getConfigurer(Class<C> clazz) {
|
||||
List<AnnotationConfigurer<O, B>> configs = this.mainConfigurers.get(clazz);
|
||||
if (configs == null) {
|
||||
return null;
|
||||
}
|
||||
if (configs.size() != 1) {
|
||||
throw new IllegalStateException("Only one configurer expected for type " + clazz + ", but got " + configs);
|
||||
}
|
||||
return (C) configs.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns the {@link AnnotationConfigurer} by its class name or
|
||||
* <code>null</code> if not found. Note that object hierarchies are not
|
||||
* considered.
|
||||
*
|
||||
* @param clazz the configurer class type
|
||||
* @param <C> type of AnnotationConfigurer
|
||||
* @return Matched configurers
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends AnnotationConfigurer<O, B>> C removeConfigurer(Class<C> clazz) {
|
||||
List<AnnotationConfigurer<O, B>> configs = this.mainConfigurers.remove(clazz);
|
||||
if (configs == null) {
|
||||
return null;
|
||||
}
|
||||
if (configs.size() != 1) {
|
||||
throw new IllegalStateException("Only one configurer expected for type " + clazz + ", but got " + configs);
|
||||
}
|
||||
return (C) configs.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the {@link ObjectPostProcessor} to use.
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} to use. Cannot be null
|
||||
* @return the {@link AnnotationBuilder} for further customizations
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public O objectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
Assert.notNull(objectPostProcessor,"objectPostProcessor cannot be null");
|
||||
this.objectPostProcessor = objectPostProcessor;
|
||||
return (O) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs post processing of an object. The default is to delegate to the
|
||||
* {@link ObjectPostProcessor}.
|
||||
*
|
||||
* @param object the Object to post process
|
||||
* @param <P> type of processed object
|
||||
* @return the possibly modified Object to use
|
||||
*/
|
||||
protected <P> P postProcess(P object) {
|
||||
return (P) this.objectPostProcessor.postProcess(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked prior to invoking each
|
||||
* {@link AnnotationConfigurer#init(AnnotationBuilder)} method. Subclasses may
|
||||
* override this method to hook into the lifecycle without using a
|
||||
* {@link AnnotationConfigurer}.
|
||||
*
|
||||
* @throws Exception if error occurred
|
||||
*/
|
||||
protected void beforeInit() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked prior to invoking each main
|
||||
* {@link AnnotationConfigurer#configure(AnnotationBuilder)} method.
|
||||
* Subclasses may override this method to hook into the lifecycle without
|
||||
* using a {@link AnnotationConfigurer}.
|
||||
*
|
||||
* @throws Exception if error occurred
|
||||
*/
|
||||
protected void beforeConfigureMains() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked prior to invoking each post
|
||||
* {@link AnnotationConfigurer#configure(AnnotationBuilder)} method.
|
||||
* Subclasses may override this method to hook into the lifecycle without
|
||||
* using a {@link AnnotationConfigurer}.
|
||||
*
|
||||
* @throws Exception if error occurred
|
||||
*/
|
||||
protected void beforeConfigurePosts() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to build the object that is being returned.
|
||||
*
|
||||
* @return Object build by this builder
|
||||
* @throws Exception if error occurred
|
||||
*/
|
||||
protected abstract O performBuild() throws Exception;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void initMainConfigurers() throws Exception {
|
||||
for (AnnotationConfigurer<O, B> configurer : getMainConfigurers()) {
|
||||
configurer.init((B) this);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void configureMainConfigurers() throws Exception {
|
||||
for (AnnotationConfigurer<O, B> configurer : getMainConfigurers()) {
|
||||
configurer.configure((B) this);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void configurePostConfigurers() throws Exception {
|
||||
for (AnnotationConfigurer<O, B> configurer : getPostConfigurers()) {
|
||||
configurer.configure((B) this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all configurers.
|
||||
*
|
||||
* @return the configurers
|
||||
*/
|
||||
private Collection<AnnotationConfigurer<O, B>> getMainConfigurers() {
|
||||
List<AnnotationConfigurer<O, B>> result = new ArrayList<AnnotationConfigurer<O, B>>();
|
||||
for (List<AnnotationConfigurer<O, B>> configs : this.mainConfigurers.values()) {
|
||||
result.addAll(configs);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Collection<AnnotationConfigurer<O, B>> getPostConfigurers() {
|
||||
List<AnnotationConfigurer<O, B>> result = new ArrayList<AnnotationConfigurer<O, B>>();
|
||||
for (List<AnnotationConfigurer<O, B>> configs : this.postConfigurers.values()) {
|
||||
result.addAll(configs);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the object is unbuilt.
|
||||
*
|
||||
* @return true, if unbuilt else false
|
||||
*/
|
||||
private boolean isUnbuilt() {
|
||||
synchronized (mainConfigurers) {
|
||||
return buildState == BuildState.UNBUILT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The build state for the application
|
||||
*/
|
||||
private static enum BuildState {
|
||||
|
||||
/**
|
||||
* This is the state before the {@link AnnotationBuilder#build()} is invoked
|
||||
*/
|
||||
UNBUILT(0),
|
||||
|
||||
/**
|
||||
* The state from when {@link AnnotationBuilder#build()} is first invoked until
|
||||
* all the {@link AnnotationConfigurer#init(AnnotationBuilder)} methods have
|
||||
* been invoked.
|
||||
*/
|
||||
INITIALIZING_MAINS(1),
|
||||
|
||||
/**
|
||||
* The state from after all main
|
||||
* {@link AnnotationConfigurer#init(AnnotationBuilder)}
|
||||
* have been invoked until after all the
|
||||
* {@link AnnotationConfigurer#configure(AnnotationBuilder)}
|
||||
* methods have been invoked.
|
||||
*/
|
||||
CONFIGURING_MAINS(2),
|
||||
|
||||
/**
|
||||
* The state from after all post
|
||||
* {@link AnnotationConfigurer#init(AnnotationBuilder)}
|
||||
* have been invoked until after all the
|
||||
* {@link AnnotationConfigurer#configure(AnnotationBuilder)}
|
||||
* methods have been invoked.
|
||||
*/
|
||||
CONFIGURING_POSTS(3),
|
||||
|
||||
/**
|
||||
* From the point after all the
|
||||
* {@link AnnotationConfigurer#configure(AnnotationBuilder)}
|
||||
* have completed to just after
|
||||
* {@link AbstractConfiguredAnnotationBuilder#performBuild()}.
|
||||
*/
|
||||
BUILDING(4),
|
||||
|
||||
/**
|
||||
* After the object has been completely built.
|
||||
*/
|
||||
BUILT(5);
|
||||
|
||||
private final int order;
|
||||
|
||||
BuildState(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if is initializing.
|
||||
*
|
||||
* @return true, if is initializing
|
||||
*/
|
||||
public boolean isInitializing() {
|
||||
return INITIALIZING_MAINS.order == order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the state is CONFIGURING or later
|
||||
*
|
||||
* @return true, if configured
|
||||
*/
|
||||
public boolean isConfigured() {
|
||||
return order >= CONFIGURING_MAINS.order;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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.config.common.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Base class for {@link Configuration} which works on a bean definition level
|
||||
* relying on {@link ImportBeanDefinitionRegistrar} phase to register beans.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <O> The object that used builder returns
|
||||
* @param <B> The type of the builder
|
||||
*/
|
||||
public abstract class AbstractImportingAnnotationConfiguration<B extends AnnotationBuilder<O>, O> implements
|
||||
ImportBeanDefinitionRegistrar, BeanFactoryAware, EnvironmentAware {
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private Environment environment;
|
||||
|
||||
private final BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
Class<?> annotationType = getAnnotation();
|
||||
AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(
|
||||
annotationType.getName(), false));
|
||||
String[] names = attributes.getStringArray("name");
|
||||
|
||||
BeanDefinition beanDefinition;
|
||||
try {
|
||||
beanDefinition = buildBeanDefinition();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error with onConfigurers", e);
|
||||
}
|
||||
|
||||
if (ObjectUtils.isEmpty(names)) {
|
||||
// ok, name(s) not given, generate one
|
||||
names = new String[] { beanNameGenerator.generateBeanName(beanDefinition, registry) };
|
||||
}
|
||||
|
||||
registry.registerBeanDefinition(names[0], beanDefinition);
|
||||
if (names.length > 1) {
|
||||
for (int i = 1; i < names.length; i++) {
|
||||
registry.registerAlias(names[0], names[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract static class BeanDelegatingFactoryBean<B extends AnnotationBuilder<O>, O>
|
||||
implements FactoryBean<O>, InitializingBean {
|
||||
|
||||
private final B builder;
|
||||
|
||||
private O object;
|
||||
|
||||
private List<AnnotationConfigurer<O, B>> configurers;
|
||||
|
||||
public BeanDelegatingFactoryBean(B builder){
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract Class<O> getObjectType();
|
||||
|
||||
@Override
|
||||
public O getObject() throws Exception {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// for (AnnotationConfigurer<O, B> configurer : configurers) {
|
||||
// if (configurer.isAssignable(builder)) {
|
||||
// // we need builder.apply(configurer);
|
||||
//// builder.
|
||||
// }
|
||||
// }
|
||||
// // should be getOrBuild???
|
||||
// object = builder.build();
|
||||
// }
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setConfigurers(List<AnnotationConfigurer<O, B>> configurers) {
|
||||
this.configurers = configurers;
|
||||
}
|
||||
|
||||
public B getBuilder() {
|
||||
return builder;
|
||||
}
|
||||
|
||||
public List<AnnotationConfigurer<O, B>> getConfigurers() {
|
||||
return configurers;
|
||||
}
|
||||
|
||||
protected void setObject(O object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
|
||||
"beanFactory be of type ListableBeanFactory but was " + beanFactory);
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to get a bean definition to register.
|
||||
*
|
||||
* @return the bean definition to register
|
||||
* @throws Exception if error occurred
|
||||
*/
|
||||
protected abstract BeanDefinition buildBeanDefinition() throws Exception;
|
||||
|
||||
/**
|
||||
* Gets the annotation specific for this configurer.
|
||||
*
|
||||
* @return the annotation
|
||||
*/
|
||||
protected abstract Class<? extends Annotation> getAnnotation();
|
||||
|
||||
/**
|
||||
* Gets the bean factory.
|
||||
*
|
||||
* @return the bean factory
|
||||
*/
|
||||
protected BeanFactory getBeanFactory() {
|
||||
return beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the environment.
|
||||
*
|
||||
* @return the environment
|
||||
*/
|
||||
protected Environment getEnvironment() {
|
||||
return environment;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.config.common.annotation;
|
||||
|
||||
/**
|
||||
* Interface for building an {@link Object}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <O> The type of the Object being built
|
||||
*/
|
||||
public interface AnnotationBuilder<O> {
|
||||
|
||||
/**
|
||||
* Builds the object and returns it or null.
|
||||
*
|
||||
* @return the Object to be built or null if the implementation allows it.
|
||||
* @throws Exception if an error occurred when building the Object
|
||||
*/
|
||||
O build() throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.config.common.annotation;
|
||||
|
||||
/**
|
||||
* Allows for configuring an {@link AnnotationBuilder}. All
|
||||
* {@link AnnotationConfigurer}s first have their {@link #init(AnnotationBuilder)}
|
||||
* method invoked. After all {@link #init(AnnotationBuilder)} methods have been
|
||||
* invoked, each {@link #configure(AnnotationBuilder)} method is invoked.
|
||||
*
|
||||
* @see AbstractConfiguredAnnotationBuilder
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <O> The object being built by the {@link AnnotationBuilder} B
|
||||
* @param <B> The {@link AnnotationBuilder} that builds objects of type O. This is
|
||||
* also the {@link AnnotationBuilder} that is being configured.
|
||||
*/
|
||||
public interface AnnotationConfigurer<O, B extends AnnotationBuilder<O>> {
|
||||
|
||||
/**
|
||||
* Initialise the {@link AnnotationBuilder}. Here only shared state should be
|
||||
* created and modified, but not properties on the {@link AnnotationBuilder}
|
||||
* used for building the object. This ensures that the
|
||||
* {@link #configure(AnnotationBuilder)} method uses the correct shared
|
||||
* objects when building.
|
||||
*
|
||||
* @param builder the builder
|
||||
* @throws Exception if error occurred
|
||||
*/
|
||||
void init(B builder) throws Exception;
|
||||
|
||||
/**
|
||||
* Configure the {@link AnnotationBuilder} by setting the necessary properties
|
||||
* on the {@link AnnotationBuilder}.
|
||||
*
|
||||
* @param builder the builder
|
||||
* @throws Exception if error occurred
|
||||
*/
|
||||
void configure(B builder) throws Exception;
|
||||
|
||||
boolean isAssignable(AnnotationBuilder<O> builder);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.config.common.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
|
||||
/**
|
||||
* A base class for {@link AnnotationConfigurer} that allows subclasses to only
|
||||
* implement the methods they are interested in. It also provides a mechanism
|
||||
* for using the {@link AnnotationConfigurer} and when done gaining access to the
|
||||
* {@link AnnotationBuilder} that is being configured.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <O> The Object being built by B
|
||||
* @param <I> The interface of type B
|
||||
* @param <B> The Builder that is building O and is configured by {@link AnnotationConfigurerAdapter}
|
||||
*/
|
||||
public abstract class AnnotationConfigurerAdapter<O,I,B extends AnnotationBuilder<O>>
|
||||
implements AnnotationConfigurer<O,B> {
|
||||
|
||||
private B builder;
|
||||
|
||||
private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();
|
||||
|
||||
@Override
|
||||
public void init(B builder) throws Exception {}
|
||||
|
||||
@Override
|
||||
public void configure(B builder) throws Exception {}
|
||||
|
||||
/**
|
||||
* Return the {@link AnnotationBuilder} when done using the
|
||||
* {@link AnnotationConfigurer}. This is useful for method chaining.
|
||||
*
|
||||
* @return the {@link AnnotationBuilder}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public I and() {
|
||||
// we're either casting to itself or its interface
|
||||
return (I) getBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link AnnotationBuilder}. Cannot be null.
|
||||
*
|
||||
* @return the {@link AnnotationBuilder}
|
||||
* @throws IllegalStateException if AnnotationBuilder is null
|
||||
*/
|
||||
protected final B getBuilder() {
|
||||
if(builder == null) {
|
||||
throw new IllegalStateException("annotationBuilder cannot be null");
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link ObjectPostProcessor} to be used for this adapter. The
|
||||
* default implementation does nothing to the object.
|
||||
*
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
|
||||
*/
|
||||
public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
|
||||
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AnnotationBuilder} to be used. This is automatically set
|
||||
* when using
|
||||
* {@link AbstractConfiguredAnnotationBuilder#apply(AnnotationConfigurerAdapter)}
|
||||
*
|
||||
* @param builder the {@link AnnotationBuilder} to set
|
||||
*/
|
||||
public void setBuilder(B builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link ObjectPostProcessor} that delegates work to numerous
|
||||
* {@link ObjectPostProcessor} implementations.
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {
|
||||
|
||||
private List<ObjectPostProcessor<? extends Object>> postProcessors = new ArrayList<ObjectPostProcessor<?>>();
|
||||
|
||||
@Override
|
||||
public Object postProcess(Object object) {
|
||||
for(ObjectPostProcessor opp : postProcessors) {
|
||||
Class<?> oppClass = opp.getClass();
|
||||
Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass,ObjectPostProcessor.class);
|
||||
if(oppType == null || oppType.isAssignableFrom(object.getClass())) {
|
||||
object = opp.postProcess(object);
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link ObjectPostProcessor} to use
|
||||
*
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} to add
|
||||
* @return true if the {@link ObjectPostProcessor} was added, else false
|
||||
*/
|
||||
private boolean addObjectPostProcessor(ObjectPostProcessor<? extends Object> objectPostProcessor) {
|
||||
return this.postProcessors.add(objectPostProcessor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssignable(AnnotationBuilder<O> builder) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.config.common.annotation;
|
||||
|
||||
/**
|
||||
* Interface for wrapping a return type from {@link AnnotationConfigurer}
|
||||
* into {@link AnnotationBuilder}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <I> The parent return type of the configurer.
|
||||
*/
|
||||
public interface AnnotationConfigurerBuilder<I> {
|
||||
|
||||
/**
|
||||
* Get a parent {@link AnnotationBuilder} working
|
||||
* with a {@link AnnotationConfigurer}.
|
||||
* @return The parent {@link AnnotationBuilder}
|
||||
*/
|
||||
I and();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.config.common.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Base annotation used in JavaConfig order to enable
|
||||
* some base functionality.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
public @interface EnableAnnotationConfiguration {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.config.common.annotation;
|
||||
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* Allows initialization of Objects. Typically this is used to call the
|
||||
* {@link Aware} methods, {@link InitializingBean#afterPropertiesSet()}, and
|
||||
* ensure that {@link DisposableBean#destroy()} has been invoked.
|
||||
*
|
||||
* @param <T> the bound of the types of Objects this {@link ObjectPostProcessor} supports.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public interface ObjectPostProcessor<T> {
|
||||
|
||||
/**
|
||||
* Initialize the object possibly returning a modified instance that should
|
||||
* be used instead.
|
||||
*
|
||||
* @param object the object to initialize
|
||||
* @param <O> the type of a processed object
|
||||
* @return the initialized version of the object
|
||||
*/
|
||||
<O extends T> O postProcess(O object);
|
||||
|
||||
/**
|
||||
* A do nothing implementation of the {@link ObjectPostProcessor}
|
||||
*/
|
||||
ObjectPostProcessor<Object> QUIESCENT_POSTPROCESSOR = new ObjectPostProcessor<Object>() {
|
||||
@Override
|
||||
public <T> T postProcess(T object) {
|
||||
return object;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.config.common.annotation.configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Post processor handling lifecycle methods of
|
||||
* POJOs created from builder/configurer.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
final class AutowireBeanFactoryObjectPostProcessor implements ObjectPostProcessor<Object>, DisposableBean, SmartLifecycle {
|
||||
|
||||
private final static Log log = LogFactory.getLog(AutowireBeanFactoryObjectPostProcessor.class);
|
||||
|
||||
private final AutowireCapableBeanFactory autowireBeanFactory;
|
||||
private final List<DisposableBean> disposableBeans = new ArrayList<DisposableBean>();
|
||||
private final List<Lifecycle> lifecycleBeans = new ArrayList<Lifecycle>();
|
||||
|
||||
private boolean running;
|
||||
|
||||
/**
|
||||
* Instantiates a new autowire bean factory object post processor.
|
||||
*
|
||||
* @param autowireBeanFactory the autowire bean factory
|
||||
*/
|
||||
public AutowireBeanFactoryObjectPostProcessor(AutowireCapableBeanFactory autowireBeanFactory) {
|
||||
Assert.notNull(autowireBeanFactory, "autowireBeanFactory cannot be null");
|
||||
this.autowireBeanFactory = autowireBeanFactory;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T postProcess(T object) {
|
||||
T result = (T) autowireBeanFactory.initializeBean(object, null);
|
||||
if(result instanceof DisposableBean) {
|
||||
disposableBeans.add((DisposableBean) result);
|
||||
}
|
||||
if(result instanceof Lifecycle) {
|
||||
lifecycleBeans.add((Lifecycle) result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
for(DisposableBean disposable : disposableBeans) {
|
||||
try {
|
||||
disposable.destroy();
|
||||
} catch(Exception error) {
|
||||
log.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
running = true;
|
||||
for (Lifecycle bean : lifecycleBeans) {
|
||||
bean.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
for (Lifecycle bean : lifecycleBeans) {
|
||||
bean.stop();
|
||||
}
|
||||
running = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
stop();
|
||||
callback.run();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.config.common.annotation.configuration;
|
||||
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.config.common.annotation.EnableAnnotationConfiguration;
|
||||
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
|
||||
|
||||
/**
|
||||
* Spring {@link Configuration} that exports the default
|
||||
* {@link ObjectPostProcessor}. This class is not intended to be imported
|
||||
* manually rather it is imported automatically when using {@link EnableAnnotationConfiguration}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @see EnableAnnotationConfiguration
|
||||
*/
|
||||
@Configuration
|
||||
public class ObjectPostProcessorConfiguration {
|
||||
|
||||
@Bean
|
||||
public ObjectPostProcessor<Object> objectPostProcessor(AutowireCapableBeanFactory beanFactory) {
|
||||
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.config.common.annotation.configurers;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationBuilder;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.statemachine.config.common.annotation.AnnotationConfigurer AnnotationConfigurer}
|
||||
* which knows how to handle configuring a {@link Properties}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <O> The Object being built by B
|
||||
* @param <I> The type of interface or builder itself returned by the configurer
|
||||
* @param <B> The Builder that is building O and is configured by {@link AnnotationConfigurerAdapter}
|
||||
*/
|
||||
public class DefaultPropertiesConfigurer<O,I,B extends AnnotationBuilder<O>>
|
||||
extends AnnotationConfigurerAdapter<O,I,B> implements PropertiesConfigurer<I> {
|
||||
|
||||
private Properties properties = new Properties();
|
||||
|
||||
/**
|
||||
* Adds a {@link Properties} to this builder.
|
||||
*
|
||||
* @param properties the properties
|
||||
* @return the {@link PropertiesConfigurer} for chaining
|
||||
*/
|
||||
@Override
|
||||
public PropertiesConfigurer<I> properties(Properties properties) {
|
||||
if (properties != null) {
|
||||
this.properties.putAll(properties);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertiesConfigurer<I> properties(Map<String, String> properties) {
|
||||
Properties props = new Properties();
|
||||
if (properties != null) {
|
||||
props.putAll(properties);
|
||||
}
|
||||
return properties(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a property to this builder.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @return the {@link PropertiesConfigurer} for chaining
|
||||
*/
|
||||
@Override
|
||||
public PropertiesConfigurer<I> property(String key, String value) {
|
||||
properties.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link Properties} configured for this builder.
|
||||
*
|
||||
* @return the properties
|
||||
*/
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(B builder) throws Exception {
|
||||
if (!configureProperties(builder, properties)) {
|
||||
if (builder instanceof PropertiesConfigurerAware) {
|
||||
((PropertiesConfigurerAware)builder).configureProperties(properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure properties. If this implementation is extended,
|
||||
* custom configure handling can be handled here.
|
||||
*
|
||||
* @param builder the builder
|
||||
* @param properties the properties
|
||||
* @return true, if properties configure is handled
|
||||
*/
|
||||
protected boolean configureProperties(B builder, Properties properties){
|
||||
return false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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.config.common.annotation.configurers;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationBuilder;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurer;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
|
||||
|
||||
/**
|
||||
* {@link AnnotationConfigurer} which knows how to handle
|
||||
* configuring a {@link Resource}s.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <O> The Object being built by B
|
||||
* @param <B> The Builder that is building O and is configured by {@link AnnotationConfigurerAdapter}
|
||||
* @param <I> The type of an interface of B
|
||||
*/
|
||||
public class DefaultResourceConfigurer<O,I,B extends AnnotationBuilder<O>>
|
||||
extends AnnotationConfigurerAdapter<O,I,B> implements ResourceConfigurer<I> {
|
||||
|
||||
private Set<Resource> resources = new HashSet<Resource>();
|
||||
private final DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
@Override
|
||||
public void configure(B builder) throws Exception {
|
||||
if (!configureResources(builder, resources)) {
|
||||
if (builder instanceof ResourceConfigurerAware) {
|
||||
((ResourceConfigurerAware)builder).configureResources(resources);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link Set} of {@link Resource}s to this builder.
|
||||
*
|
||||
* @param resources the resources
|
||||
* @return the {@link ResourceConfigurer} for chaining
|
||||
*/
|
||||
@Override
|
||||
public ResourceConfigurer<I> resources(Set<Resource> resources) {
|
||||
this.resources.addAll(resources);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link Resource} to this builder.
|
||||
*
|
||||
* @param resource the resource
|
||||
* @return the {@link ResourceConfigurer} for chaining
|
||||
*/
|
||||
@Override
|
||||
public ResourceConfigurer<I> resource(Resource resource) {
|
||||
resources.add(resource);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link Resource} to this builder.
|
||||
*
|
||||
* @param resource the resource
|
||||
* @return the {@link ResourceConfigurer} for chaining
|
||||
*/
|
||||
@Override
|
||||
public ResourceConfigurer<I> resource(String resource) {
|
||||
resources.add(resourceLoader.getResource(resource));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link Resource}s to this builder.
|
||||
*
|
||||
* @param resources the resources
|
||||
* @return the {@link ResourceConfigurer} for chaining
|
||||
*/
|
||||
@Override
|
||||
public ResourceConfigurer<I> resources(List<String> resources) {
|
||||
if (resources != null) {
|
||||
for (String resource : resources) {
|
||||
resource(resource);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link Resource}s configured for this builder.
|
||||
*
|
||||
* @return the resources
|
||||
*/
|
||||
public Set<Resource> getResources() {
|
||||
return resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure resources. If this implementation is extended,
|
||||
* custom configure handling can be handled here.
|
||||
*
|
||||
* @param builder the builder
|
||||
* @param resources the resources
|
||||
* @return true, if resources configure is handled
|
||||
*/
|
||||
protected boolean configureResources(B builder, Set<Resource> resources){
|
||||
return false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.config.common.annotation.configurers;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder;
|
||||
|
||||
/**
|
||||
* Interface for {@link DefaultPropertiesConfigurer} which act
|
||||
* as intermediate gatekeeper between a user and
|
||||
* an {@link org.springframework.statemachine.config.common.annotation.AnnotationConfigurer}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <I> The parent return type of the configurer.
|
||||
*/
|
||||
public interface PropertiesConfigurer<I> extends AnnotationConfigurerBuilder<I> {
|
||||
|
||||
PropertiesConfigurer<I> properties(Properties properties);
|
||||
|
||||
PropertiesConfigurer<I> properties(Map<String, String> properties);
|
||||
|
||||
PropertiesConfigurer<I> property(String key, String value);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.config.common.annotation.configurers;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Interface for {@link org.springframework.statemachine.config.common.annotation.AnnotationBuilder AnnotationBuilder}
|
||||
* which wants to be aware of {@link Properties} configured by {@link DefaultPropertiesConfigurer}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public interface PropertiesConfigurerAware {
|
||||
|
||||
/**
|
||||
* Configure {@link Properties}.
|
||||
*
|
||||
* @param properties the properties
|
||||
*/
|
||||
void configureProperties(Properties properties);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.config.common.annotation.configurers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder;
|
||||
|
||||
/**
|
||||
* Interface for {@link DefaultResourceConfigurer} which act
|
||||
* as intermediate gatekeeper between a user and
|
||||
* an {@link org.springframework.statemachine.config.common.annotation.AnnotationConfigurer}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <I> The parent return type of the configurer.
|
||||
*/
|
||||
public interface ResourceConfigurer<I> extends AnnotationConfigurerBuilder<I> {
|
||||
|
||||
ResourceConfigurer<I> resources(Set<Resource> resources);
|
||||
|
||||
ResourceConfigurer<I> resources(List<String> resources);
|
||||
|
||||
ResourceConfigurer<I> resource(Resource resource);
|
||||
|
||||
ResourceConfigurer<I> resource(String resource);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.config.common.annotation.configurers;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationBuilder;
|
||||
|
||||
/**
|
||||
* Interface for {@link AnnotationBuilder} which wants to be
|
||||
* aware of {@link Resource}s configured by {@link DefaultResourceConfigurer}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public interface ResourceConfigurerAware {
|
||||
|
||||
/**
|
||||
* Configure {@link Resource}s.
|
||||
*
|
||||
* @param resources the resources
|
||||
*/
|
||||
void configureResources(Set<Resource> resources);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package org.springframework.statemachine.config.configuration;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnumStateMachineFactory;
|
||||
import org.springframework.statemachine.config.StateMachineConfig;
|
||||
import org.springframework.statemachine.config.builders.StateMachineConfigBuilder;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStates;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitions;
|
||||
import org.springframework.statemachine.config.common.annotation.AbstractImportingAnnotationConfiguration;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurer;
|
||||
import org.springframework.statemachine.state.State;
|
||||
|
||||
@Configuration
|
||||
public class StateMachineConfiguration<S extends Enum<S>, E extends Enum<E>> extends
|
||||
AbstractImportingAnnotationConfiguration<StateMachineConfigBuilder<S, E>, StateMachineConfig<S, E>> {
|
||||
|
||||
private final StateMachineConfigBuilder<S, E> builder = new StateMachineConfigBuilder<S, E>();
|
||||
|
||||
@Override
|
||||
protected BeanDefinition buildBeanDefinition() throws Exception {
|
||||
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(StateMachineDelegatingFactoryBean.class);
|
||||
beanDefinitionBuilder.addConstructorArgValue(builder);
|
||||
return beanDefinitionBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends Annotation> getAnnotation() {
|
||||
return EnableStateMachine.class;
|
||||
}
|
||||
|
||||
private static class StateMachineDelegatingFactoryBean<S extends Enum<S>, E extends Enum<E>> implements
|
||||
FactoryBean<StateMachine<State<S, E>, E>>, BeanFactoryAware, InitializingBean {
|
||||
|
||||
private final StateMachineConfigBuilder<S, E> builder;
|
||||
|
||||
private List<AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>>> configurers;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private StateMachine<State<S, E>, E> stateMachine;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public StateMachineDelegatingFactoryBean(StateMachineConfigBuilder<S, E> builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateMachine<State<S, E>, E> getObject() throws Exception {
|
||||
return stateMachine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return StateMachine.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
for (AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>> configurer : configurers) {
|
||||
builder.apply(configurer);
|
||||
}
|
||||
StateMachineConfig<S, E> stateMachineConfig = builder.getOrBuild();
|
||||
StateMachineTransitions<S, E> stateMachineTransitions = stateMachineConfig.getTransitions();
|
||||
StateMachineStates<S, E> stateMachineStates = stateMachineConfig.getStates();
|
||||
EnumStateMachineFactory<S,E> stateMachineFactory = new EnumStateMachineFactory<S, E>(stateMachineTransitions, stateMachineStates);
|
||||
stateMachineFactory.setBeanFactory(beanFactory);
|
||||
stateMachine = stateMachineFactory.getStateMachine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Autowired(required=false)
|
||||
protected void onConfigurers(
|
||||
List<AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>>> configurers)
|
||||
throws Exception {
|
||||
this.configurers = configurers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package org.springframework.statemachine.config.configuration;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.config.EnableStateMachineFactory;
|
||||
import org.springframework.statemachine.config.EnumStateMachineFactory;
|
||||
import org.springframework.statemachine.config.StateMachineConfig;
|
||||
import org.springframework.statemachine.config.StateMachineFactory;
|
||||
import org.springframework.statemachine.config.builders.StateMachineConfigBuilder;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStates;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitions;
|
||||
import org.springframework.statemachine.config.common.annotation.AbstractImportingAnnotationConfiguration;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurer;
|
||||
import org.springframework.statemachine.state.State;
|
||||
|
||||
@Configuration
|
||||
public class StateMachineFactoryConfiguration<S extends Enum<S>, E extends Enum<E>> extends
|
||||
AbstractImportingAnnotationConfiguration<StateMachineConfigBuilder<S, E>, StateMachineConfig<S, E>> {
|
||||
|
||||
private final StateMachineConfigBuilder<S, E> builder = new StateMachineConfigBuilder<S, E>();
|
||||
|
||||
@Override
|
||||
protected BeanDefinition buildBeanDefinition() throws Exception {
|
||||
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(StateMachineFactoryDelegatingFactoryBean.class);
|
||||
beanDefinitionBuilder.addConstructorArgValue(builder);
|
||||
return beanDefinitionBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends Annotation> getAnnotation() {
|
||||
return EnableStateMachineFactory.class;
|
||||
}
|
||||
|
||||
private static class StateMachineFactoryDelegatingFactoryBean<S extends Enum<S>, E extends Enum<E>> implements
|
||||
FactoryBean<StateMachineFactory<State<S, E>, E>>, BeanFactoryAware, InitializingBean {
|
||||
|
||||
private final StateMachineConfigBuilder<S, E> builder;
|
||||
|
||||
private List<AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>>> configurers;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private StateMachineFactory<State<S, E>, E> stateMachineFactory;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public StateMachineFactoryDelegatingFactoryBean(StateMachineConfigBuilder<S, E> builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateMachineFactory<State<S, E>, E> getObject() throws Exception {
|
||||
return stateMachineFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return StateMachineFactory.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
for (AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>> configurer : configurers) {
|
||||
builder.apply(configurer);
|
||||
}
|
||||
StateMachineConfig<S, E> stateMachineConfig = builder.getOrBuild();
|
||||
StateMachineTransitions<S, E> stateMachineTransitions = stateMachineConfig.getTransitions();
|
||||
StateMachineStates<S, E> stateMachineStates = stateMachineConfig.getStates();
|
||||
EnumStateMachineFactory<S,E> enumStateMachineFactory = new EnumStateMachineFactory<S, E>(stateMachineTransitions, stateMachineStates);
|
||||
enumStateMachineFactory.setBeanFactory(beanFactory);
|
||||
this.stateMachineFactory = enumStateMachineFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Autowired(required=false)
|
||||
protected void onConfigurers(
|
||||
List<AnnotationConfigurer<StateMachineConfig<S, E>, StateMachineConfigBuilder<S, E>>> configurers)
|
||||
throws Exception {
|
||||
this.configurers = configurers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.config.configurers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.expression.spel.SpelCompilerMode;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionBuilder;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitions;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.guard.SpelExpressionGuard;
|
||||
import org.springframework.statemachine.transition.TransitionKind;
|
||||
|
||||
/**
|
||||
* Default implementation of a {@link ExternalTransitionConfigurer}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class DefaultExternalTransitionConfigurer<S, E>
|
||||
extends AnnotationConfigurerAdapter<StateMachineTransitions<S, E>, StateMachineTransitionConfigurer<S, E>, StateMachineTransitionBuilder<S, E>>
|
||||
implements ExternalTransitionConfigurer<S, E> {
|
||||
|
||||
private S source;
|
||||
|
||||
private S target;
|
||||
|
||||
private E event;
|
||||
|
||||
private Collection<Action> actions = new ArrayList<Action>();
|
||||
|
||||
private Guard guard;
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionBuilder<S, E> builder) throws Exception {
|
||||
builder.add(source, target, event, actions, guard, TransitionKind.EXTERNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternalTransitionConfigurer<S, E> source(S source) {
|
||||
this.source = source;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternalTransitionConfigurer<S, E> target(S target) {
|
||||
this.target = target;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternalTransitionConfigurer<S, E> event(E event) {
|
||||
this.event = event;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternalTransitionConfigurer<S, E> action(Action action) {
|
||||
actions.add(action);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternalTransitionConfigurer<S, E> guard(Guard guard) {
|
||||
this.guard = guard;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternalTransitionConfigurer<S, E> guardExpression(String expression) {
|
||||
SpelExpressionParser parser = new SpelExpressionParser(
|
||||
new SpelParserConfiguration(SpelCompilerMode.MIXED, null));
|
||||
this.guard = new SpelExpressionGuard(parser.parseExpression(expression));
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.config.configurers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.expression.spel.SpelCompilerMode;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionBuilder;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitions;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.guard.SpelExpressionGuard;
|
||||
import org.springframework.statemachine.transition.TransitionKind;
|
||||
|
||||
/**
|
||||
* Default implementation of a {@link InternalTransitionConfigurer}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class DefaultInternalTransitionConfigurer<S, E>
|
||||
extends AnnotationConfigurerAdapter<StateMachineTransitions<S, E>, StateMachineTransitionConfigurer<S, E>, StateMachineTransitionBuilder<S, E>>
|
||||
implements InternalTransitionConfigurer<S, E> {
|
||||
|
||||
private S source;
|
||||
|
||||
private S target;
|
||||
|
||||
private E event;
|
||||
|
||||
private Collection<Action> actions = new ArrayList<Action>();
|
||||
|
||||
private Guard guard;
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionBuilder<S, E> builder) throws Exception {
|
||||
builder.add(source, target, event, actions, guard, TransitionKind.INTERNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTransitionConfigurer<S, E> source(S source) {
|
||||
this.source = source;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTransitionConfigurer<S, E> event(E event) {
|
||||
this.event = event;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTransitionConfigurer<S, E> action(Action action) {
|
||||
actions.add(action);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTransitionConfigurer<S, E> guard(Guard guard) {
|
||||
this.guard = guard;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTransitionConfigurer<S, E> guardExpression(String expression) {
|
||||
SpelExpressionParser parser = new SpelExpressionParser(
|
||||
new SpelParserConfiguration(SpelCompilerMode.MIXED, null));
|
||||
this.guard = new SpelExpressionGuard(parser.parseExpression(expression));
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.config.configurers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateBuilder;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStates;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStates.StateData;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
|
||||
|
||||
public class DefaultStateConfigurer<S, E>
|
||||
extends AnnotationConfigurerAdapter<StateMachineStates<S, E>, StateMachineStateConfigurer<S, E>, StateMachineStateBuilder<S, E>>
|
||||
implements StateConfigurer<S, E> {
|
||||
|
||||
private final Collection<StateData<S, E>> states = new ArrayList<StateData<S, E>>();
|
||||
|
||||
private S initial;
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateBuilder<S, E> builder) throws Exception {
|
||||
builder.add(states);
|
||||
builder.setInitialState(initial);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> initial(S initial) {
|
||||
this.initial = initial;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> state(S state) {
|
||||
return state(state, (E[])null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> state(S state, Collection<Action> entryActions, Collection<Action> exitActions) {
|
||||
states.add(new StateData<S, E>(state, null, entryActions, exitActions));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> state(S state, E... deferred) {
|
||||
states.add(new StateData<S, E>(state, deferred));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> states(Set<S> states) {
|
||||
for (S s : states) {
|
||||
state(s);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.config.configurers;
|
||||
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* {@code TransitionConfigurer} interface for configuring external {@link Transition}s.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public interface ExternalTransitionConfigurer<S, E> extends
|
||||
TransitionConfigurer<ExternalTransitionConfigurer<S, E>, S, E> {
|
||||
|
||||
/**
|
||||
* Specify a target state {@code S} for this {@link Transition}.
|
||||
*
|
||||
* @param target the target state {@code S}
|
||||
* @return configurer for chaining
|
||||
*/
|
||||
ExternalTransitionConfigurer<S, E> target(S target);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.config.configurers;
|
||||
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* {@code TransitionConfigurer} interface for configuring internal {@link Transition}s.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public interface InternalTransitionConfigurer<S, E> extends
|
||||
TransitionConfigurer<InternalTransitionConfigurer<S, E>, S, E> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.config.configurers;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder;
|
||||
|
||||
public interface StateConfigurer<S, E> extends
|
||||
AnnotationConfigurerBuilder<StateMachineStateConfigurer<S, E>> {
|
||||
|
||||
StateConfigurer<S, E> initial(S initial);
|
||||
|
||||
StateConfigurer<S, E> state(S state);
|
||||
|
||||
StateConfigurer<S, E> state(S state, Collection<Action> entryActions, Collection<Action> exitActions);
|
||||
|
||||
StateConfigurer<S, E> state(S state, E... deferred);
|
||||
|
||||
StateConfigurer<S, E> states(Set<S> states);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.config.configurers;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* Base {@code TransitionConfigurer} interface for configuring {@link Transition}s.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <T> the type of a transition configurer
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public interface TransitionConfigurer<T, S, E> extends
|
||||
AnnotationConfigurerBuilder<StateMachineTransitionConfigurer<S, E>> {
|
||||
|
||||
/**
|
||||
* Specify a source state {@code S} for this {@link Transition}.
|
||||
*
|
||||
* @param source the source state {@code S}
|
||||
* @return configurer for chaining
|
||||
*/
|
||||
T source(S source);
|
||||
|
||||
/**
|
||||
* Specify event {@code E} for this {@link Transition}.
|
||||
*
|
||||
* @param event the event for transition
|
||||
* @return configurer for chaining
|
||||
*/
|
||||
T event(E event);
|
||||
|
||||
/**
|
||||
* Specify {@link Action} for this {@link Transition}.
|
||||
*
|
||||
* @param action the action
|
||||
* @return configurer for chaining
|
||||
*/
|
||||
T action(Action action);
|
||||
|
||||
/**
|
||||
* Specify a {@link Guard} for this {@link Transition}.
|
||||
*
|
||||
* @param guard the guard
|
||||
* @return configurer for chaining
|
||||
*/
|
||||
T guard(Guard guard);
|
||||
|
||||
/**
|
||||
* Specify a {@link Guard} backed by a SpEL expression for this {@link Transition}.
|
||||
*
|
||||
* @param expression the SpEL expression
|
||||
* @return configurer for chaining
|
||||
*/
|
||||
T guardExpression(String expression);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.guard;
|
||||
|
||||
import org.springframework.statemachine.StateContext;
|
||||
|
||||
/**
|
||||
* {@code Guard}s are typically considered as guard conditions which affect the
|
||||
* behaviour of a state machine by enabling actions or transitions only when they
|
||||
* evaluate to {@code TRUE} and disabling them when they evaluate to
|
||||
* {@code FALSE}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public interface Guard {
|
||||
|
||||
/**
|
||||
* Evaluate a guard condition.
|
||||
*
|
||||
* @param context the state context
|
||||
* @return true, if guard evaluation is successful, false otherwise.
|
||||
*/
|
||||
boolean evaluate(StateContext context);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.guard;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Guard} which uses Spring SpEL expression for condition evaluation.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class SpelExpressionGuard implements Guard {
|
||||
|
||||
private final Expression expression;
|
||||
|
||||
/**
|
||||
* Instantiates a new spel expression guard.
|
||||
*
|
||||
* @param expression the expression
|
||||
*/
|
||||
public SpelExpressionGuard(Expression expression) {
|
||||
Assert.notNull(expression, "Expression cannot be null");
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(StateContext context) {
|
||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(context);
|
||||
return expression.getValue(evaluationContext, Boolean.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base implementation for all composite listeners.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <T> the type of the listener
|
||||
*/
|
||||
public class AbstractCompositeListener<T> {
|
||||
|
||||
/** List of ordered composite listeners */
|
||||
private OrderedComposite<T> listeners;
|
||||
|
||||
/**
|
||||
* Constructs instance with an empty listener list.
|
||||
*/
|
||||
public AbstractCompositeListener() {
|
||||
listeners = new OrderedComposite<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of listeners. This clears
|
||||
* all existing listeners.
|
||||
*
|
||||
* @param listeners the new listeners
|
||||
*/
|
||||
public void setListeners(List<? extends T> listeners) {
|
||||
this.listeners.setItems(listeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new listener.
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
public void register(T listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the listeners.
|
||||
*
|
||||
* @return the listeners
|
||||
*/
|
||||
public OrderedComposite<T> getListeners() {
|
||||
return listeners;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.springframework.statemachine.state.State;
|
||||
|
||||
public class CompositeStateMachineListener<S,E> extends AbstractCompositeListener<StateMachineListener<State<S,E>, E>> implements
|
||||
StateMachineListener<State<S,E>, 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();
|
||||
listener.stateChanged(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
/**
|
||||
* Composite item which can be used in other components which
|
||||
* may want to allow automatic and annotation based ordering.
|
||||
* Good use case is a list of listeners where user may want
|
||||
* to place some of them to be processed before the others.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of the item
|
||||
*/
|
||||
public class OrderedComposite<S> {
|
||||
|
||||
private List<S> unordered = new ArrayList<S>();
|
||||
|
||||
private List<S> ordered = new ArrayList<S>();
|
||||
|
||||
private Comparator<? super S> comparator = new AnnotationAwareOrderComparator();
|
||||
|
||||
private List<S> list = new ArrayList<S>();
|
||||
|
||||
/**
|
||||
* Public setter for the listeners.
|
||||
*
|
||||
* @param items items
|
||||
*/
|
||||
public void setItems(List<? extends S> items) {
|
||||
unordered.clear();
|
||||
ordered.clear();
|
||||
for (S s : items) {
|
||||
add(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register additional item.
|
||||
*
|
||||
* @param item item
|
||||
*/
|
||||
public void add(S item) {
|
||||
if (item instanceof Ordered) {
|
||||
if (!ordered.contains(item)) {
|
||||
ordered.add(item);
|
||||
}
|
||||
} else if (AnnotationUtils.isAnnotationDeclaredLocally(Order.class, item.getClass())) {
|
||||
if (!ordered.contains(item)) {
|
||||
ordered.add(item);
|
||||
}
|
||||
} else if (!unordered.contains(item)) {
|
||||
unordered.add(item);
|
||||
}
|
||||
Collections.sort(ordered, comparator);
|
||||
list.clear();
|
||||
list.addAll(ordered);
|
||||
list.addAll(unordered);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public getter for the list of items. The {@link Ordered} items come
|
||||
* first, followed by any unordered ones.
|
||||
*
|
||||
* @return an iterator over the list of items
|
||||
*/
|
||||
public Iterator<S> iterator() {
|
||||
return new ArrayList<S>(list).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Public getter for the list of items in reverse. The {@link Ordered} items
|
||||
* come last, after any unordered ones.
|
||||
*
|
||||
* @return an iterator over the list of items
|
||||
*/
|
||||
public Iterator<S> reverse() {
|
||||
ArrayList<S> result = new ArrayList<S>(list);
|
||||
Collections.reverse(result);
|
||||
return result.iterator();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
public interface StateMachineListener<S,E> {
|
||||
|
||||
void stateChanged(S from, S to);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Strategy interface for post-processing annotated methods.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <T> the type of an annotation
|
||||
*/
|
||||
public interface MethodAnnotationPostProcessor<T extends Annotation> {
|
||||
|
||||
/**
|
||||
* Post process a bean. As a result of a given bean, its name, method and
|
||||
* annotation in a method, this method can return a new bean or
|
||||
* <code>null</code>. Caller of this method is then responsible to handle
|
||||
* newly created object.
|
||||
*
|
||||
* @param bean the bean
|
||||
* @param beanName the bean name
|
||||
* @param method the method
|
||||
* @param annotation the annotation
|
||||
* @return the object
|
||||
*/
|
||||
Object postProcess(Object bean, String beanName, Method method, T annotation);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* A simple {@link StateMachineRuntimeProcessor} implementation using
|
||||
* methods from a state machine protected bean.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <T> the return type
|
||||
*/
|
||||
public class MethodInvokingStateMachineRuntimeProcessor<T> implements StateMachineRuntimeProcessor<T> {
|
||||
|
||||
private final StateMachineMethodInvokerHelper<T> delegate;
|
||||
|
||||
public MethodInvokingStateMachineRuntimeProcessor(Object targetObject, Method method) {
|
||||
delegate = new StateMachineMethodInvokerHelper<T>(targetObject, method);
|
||||
}
|
||||
|
||||
public MethodInvokingStateMachineRuntimeProcessor(Object targetObject, String methodName) {
|
||||
delegate = new StateMachineMethodInvokerHelper<T>(targetObject, methodName);
|
||||
}
|
||||
|
||||
public MethodInvokingStateMachineRuntimeProcessor(Object targetObject, Class<? extends Annotation> annotationType) {
|
||||
delegate = new StateMachineMethodInvokerHelper<T>(targetObject, annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T process(StateMachineRuntime stateMachineRuntime) {
|
||||
try {
|
||||
return delegate.process(stateMachineRuntime);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error processing bean", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.annotation.OrderUtils;
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
|
||||
/**
|
||||
* Post-processor for Methods annotated with {@link OnTransition}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class StateMachineActivatorAnnotationPostProcessor implements MethodAnnotationPostProcessor<OnTransition>{
|
||||
|
||||
protected final BeanFactory beanFactory;
|
||||
|
||||
public StateMachineActivatorAnnotationPostProcessor(ListableBeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcess(Object bean, String beanName, Method method, OnTransition annotation) {
|
||||
StateMachineHandler handler = new StateMachineOnTransitionHandler(bean, method, annotation);
|
||||
|
||||
Integer order = findOrder(bean, method);
|
||||
if (order != null) {
|
||||
handler.setOrder(order);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find {@link Order} order either from a class or
|
||||
* method level. Method level always takes presence
|
||||
* over class level.
|
||||
*
|
||||
* @param bean the bean to inspect
|
||||
* @param method the method to inspect
|
||||
* @return the order or NULL if not found
|
||||
*/
|
||||
private static Integer findOrder(Object bean, Method method) {
|
||||
Integer order = OrderUtils.getOrder(bean.getClass());
|
||||
Order ann = AnnotationUtils.findAnnotation(method, Order.class);
|
||||
if (ann != null) {
|
||||
order = ann.value();
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* A {@link BeanPostProcessor} implementation that processes method-level
|
||||
* annotations such as {@link OnTransition}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Marius Bogoevici
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class StateMachineAnnotationPostProcessor implements BeanPostProcessor, BeanFactoryAware, InitializingBean,
|
||||
Lifecycle, ApplicationListener<ApplicationEvent> {
|
||||
|
||||
private final static Log log = LogFactory.getLog(StateMachineAnnotationPostProcessor.class);
|
||||
|
||||
/** Factory from BeanFactoryAware */
|
||||
private volatile ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
/** Post processors map - annotation -> method post processor */
|
||||
private final Map<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>> postProcessors =
|
||||
new HashMap<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>>();
|
||||
|
||||
/**
|
||||
* Application events for post processed beans (if bean instance of ApplicationListener) will
|
||||
* be dispatched from here via callback in this class.
|
||||
*/
|
||||
private final Set<ApplicationListener<ApplicationEvent>> listeners = new HashSet<ApplicationListener<ApplicationEvent>>();
|
||||
|
||||
/**
|
||||
* Lifecycle callbacks for post processed bean (if bean instance of Lifecycle) will
|
||||
* be dispatched from here via callback in this class.
|
||||
*/
|
||||
private final Set<Lifecycle> lifecycles = new HashSet<Lifecycle>();
|
||||
|
||||
/** Flag for Lifecycle in this class */
|
||||
private volatile boolean running = true;
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
Assert.isAssignable(ConfigurableListableBeanFactory.class, beanFactory.getClass(),
|
||||
"a ConfigurableListableBeanFactory is required");
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
postProcessors.put(OnTransition.class, new StateMachineActivatorAnnotationPostProcessor(beanFactory));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
final Class<?> beanClass = getBeanClass(bean);
|
||||
|
||||
if (!isStereotype(beanClass)) {
|
||||
// we only post-process stereotype components
|
||||
return bean;
|
||||
}
|
||||
|
||||
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
|
||||
|
||||
for (Annotation annotation : annotations) {
|
||||
MethodAnnotationPostProcessor postProcessor = postProcessors.get(annotation.annotationType());
|
||||
|
||||
if (postProcessor != null && shouldCreateHandler(annotation)) {
|
||||
Object result = postProcessor.postProcess(bean, beanName, method, annotation);
|
||||
|
||||
if (result != null && result instanceof StateMachineHandler) {
|
||||
String endpointBeanName = generateBeanName(beanName, method, annotation.annotationType());
|
||||
|
||||
if (result instanceof BeanNameAware) {
|
||||
((BeanNameAware) result).setBeanName(endpointBeanName);
|
||||
}
|
||||
beanFactory.registerSingleton(endpointBeanName, result);
|
||||
if (result instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) result).setBeanFactory(beanFactory);
|
||||
}
|
||||
if (result instanceof InitializingBean) {
|
||||
try {
|
||||
((InitializingBean) result).afterPropertiesSet();
|
||||
} catch (Exception e) {
|
||||
throw new BeanInitializationException("failed to initialize annotated component", e);
|
||||
}
|
||||
}
|
||||
if (result instanceof Lifecycle) {
|
||||
lifecycles.add((Lifecycle) result);
|
||||
if (result instanceof SmartLifecycle && ((SmartLifecycle) result).isAutoStartup()) {
|
||||
((SmartLifecycle) result).start();
|
||||
}
|
||||
}
|
||||
if (result instanceof ApplicationListener) {
|
||||
listeners.add((ApplicationListener) result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
for (ApplicationListener<ApplicationEvent> listener : listeners) {
|
||||
try {
|
||||
listener.onApplicationEvent(event);
|
||||
} catch (ClassCastException e) {
|
||||
if (log.isWarnEnabled() && event != null) {
|
||||
log.warn("ApplicationEvent of type [" + event.getClass()
|
||||
+ "] not accepted by ApplicationListener [" + listener + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
for (Lifecycle lifecycle : this.lifecycles) {
|
||||
if (!lifecycle.isRunning()) {
|
||||
lifecycle.start();
|
||||
}
|
||||
}
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
for (Lifecycle lifecycle : this.lifecycles) {
|
||||
if (lifecycle.isRunning()) {
|
||||
lifecycle.stop();
|
||||
}
|
||||
}
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
private boolean shouldCreateHandler(Annotation annotation) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bean class. Will check if bean is a proxy and
|
||||
* find a class from there as target class, otherwise
|
||||
* we just get bean class.
|
||||
*
|
||||
* @param bean the bean
|
||||
* @return the bean class
|
||||
*/
|
||||
private Class<?> getBeanClass(Object bean) {
|
||||
Class<?> targetClass = AopUtils.getTargetClass(bean);
|
||||
return (targetClass != null) ? targetClass : bean.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if class is a stereotype meaning if there is
|
||||
* a Component annotation present.
|
||||
*
|
||||
* @param beanClass the bean class
|
||||
* @return true, if is stereotype
|
||||
*/
|
||||
private boolean isStereotype(Class<?> beanClass) {
|
||||
List<Annotation> annotations = new ArrayList<Annotation>(Arrays.asList(beanClass.getAnnotations()));
|
||||
Class<?>[] interfaces = beanClass.getInterfaces();
|
||||
for (Class<?> iface : interfaces) {
|
||||
annotations.addAll(Arrays.asList(iface.getAnnotations()));
|
||||
}
|
||||
for (Annotation annotation : annotations) {
|
||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
if (annotationType.equals(Component.class) || annotationType.isAnnotationPresent(Component.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String generateBeanName(String originalBeanName, Method method, Class<? extends Annotation> annotationType) {
|
||||
String baseName = originalBeanName + "." + method.getName() + "." + ClassUtils.getShortNameAsProperty(annotationType);
|
||||
String name = baseName;
|
||||
int count = 1;
|
||||
while (beanFactory.containsBean(name)) {
|
||||
name = baseName + "#" + (++count);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
import org.springframework.statemachine.annotation.WithStateMachine;
|
||||
|
||||
/**
|
||||
* Handler for a common object representing something to be run.
|
||||
* This is usually used when a plain pojo is configured with {@link WithStateMachine}
|
||||
* and {@link OnTransition} annotations.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class StateMachineHandler implements Ordered {
|
||||
|
||||
private final StateMachineRuntimeProcessor<?> processor;
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
/**
|
||||
* Instantiates a new container handler.
|
||||
*
|
||||
* @param target the target bean
|
||||
*/
|
||||
// public StateMachineHandler(Object target) {
|
||||
// this(new MethodInvokingStateMachineRuntimeProcessor<Object>(target, OnTransition.class));
|
||||
// }
|
||||
|
||||
/**
|
||||
* Instantiates a new container handler.
|
||||
*
|
||||
* @param target the target bean
|
||||
* @param method the method
|
||||
*/
|
||||
public StateMachineHandler(Object target, Method method) {
|
||||
this(new MethodInvokingStateMachineRuntimeProcessor<Object>(target, method));
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new container handler.
|
||||
*
|
||||
* @param target the target bean
|
||||
* @param methodName the method name
|
||||
*/
|
||||
public StateMachineHandler(Object target, String methodName) {
|
||||
this(new MethodInvokingStateMachineRuntimeProcessor<Object>(target, methodName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new container handler.
|
||||
*
|
||||
* @param <T> the generic type
|
||||
* @param processor the processor
|
||||
*/
|
||||
public <T> StateMachineHandler(MethodInvokingStateMachineRuntimeProcessor<T> processor) {
|
||||
this.processor = processor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the order used get value from {@link #getOrder()}.
|
||||
* Default value is {@link Ordered#LOWEST_PRECEDENCE}.
|
||||
*
|
||||
* @param order the new order
|
||||
*/
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle container using a {@link StateMachineRuntimeProcessor}.
|
||||
*
|
||||
* @param stateMachineRuntime the state machine runtime
|
||||
* @return the result value
|
||||
*/
|
||||
public Object handle(StateMachineRuntime stateMachineRuntime) {
|
||||
return processor.process(stateMachineRuntime);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.support.AbstractExpressionEvaluator;
|
||||
import org.springframework.statemachine.support.AnnotatedMethodFilter;
|
||||
import org.springframework.statemachine.support.FixedMethodFilter;
|
||||
import org.springframework.statemachine.support.UniqueMethodFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
|
||||
/**
|
||||
* A helper class using spel to execute target methods.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <T> the return type
|
||||
*/
|
||||
public class StateMachineMethodInvokerHelper<T> extends AbstractExpressionEvaluator {
|
||||
|
||||
private static final String CANDIDATE_METHODS = "CANDIDATE_METHODS";
|
||||
|
||||
private static final String CANDIDATE_MESSAGE_METHODS = "CANDIDATE_MESSAGE_METHODS";
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final Object targetObject;
|
||||
|
||||
private volatile String displayString;
|
||||
|
||||
private volatile boolean requiresReply;
|
||||
|
||||
private final Map<Class<?>, HandlerMethod> handlerMethods;
|
||||
|
||||
private final Map<Class<?>, HandlerMethod> handlerMessageMethods;
|
||||
|
||||
private final LinkedList<Map<Class<?>, HandlerMethod>> handlerMethodsList;
|
||||
|
||||
private final HandlerMethod handlerMethod;
|
||||
|
||||
private final Class<?> expectedType;
|
||||
|
||||
public StateMachineMethodInvokerHelper(Object targetObject, Method method) {
|
||||
this(targetObject, method, null);
|
||||
}
|
||||
|
||||
public StateMachineMethodInvokerHelper(Object targetObject, Method method, Class<?> expectedType) {
|
||||
this(targetObject, null, method, expectedType);
|
||||
}
|
||||
|
||||
public StateMachineMethodInvokerHelper(Object targetObject, String methodName) {
|
||||
this(targetObject, methodName, null);
|
||||
}
|
||||
|
||||
public StateMachineMethodInvokerHelper(Object targetObject, String methodName, Class<?> expectedType) {
|
||||
this(targetObject, null, methodName, expectedType);
|
||||
}
|
||||
|
||||
public StateMachineMethodInvokerHelper(Object targetObject, Class<? extends Annotation> annotationType) {
|
||||
this(targetObject, annotationType, null);
|
||||
}
|
||||
|
||||
public StateMachineMethodInvokerHelper(Object targetObject, Class<? extends Annotation> annotationType,
|
||||
Class<?> expectedType) {
|
||||
this(targetObject, annotationType, (String) null, expectedType);
|
||||
}
|
||||
|
||||
public T process(StateMachineRuntime stateMachineRuntime) throws Exception {
|
||||
ParametersWrapper wrapper = new ParametersWrapper(stateMachineRuntime.getStateContext());
|
||||
return processInternal(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.displayString;
|
||||
}
|
||||
|
||||
private StateMachineMethodInvokerHelper(Object targetObject, Class<? extends Annotation> annotationType,
|
||||
Method method, Class<?> expectedType) {
|
||||
Assert.notNull(method, "method must not be null");
|
||||
this.expectedType = expectedType;
|
||||
this.requiresReply = expectedType != null;
|
||||
if (expectedType != null) {
|
||||
Assert.isTrue(method.getReturnType() != Void.class && method.getReturnType() != Void.TYPE,
|
||||
"method must have a return type");
|
||||
}
|
||||
Assert.notNull(targetObject, "targetObject must not be null");
|
||||
this.targetObject = targetObject;
|
||||
this.handlerMethod = new HandlerMethod(method);
|
||||
this.handlerMethods = null;
|
||||
this.handlerMessageMethods = null;
|
||||
this.handlerMethodsList = null;
|
||||
this.prepareEvaluationContext(this.getEvaluationContext(false), method, annotationType);
|
||||
this.setDisplayString(targetObject, method);
|
||||
}
|
||||
|
||||
private StateMachineMethodInvokerHelper(Object targetObject, Class<? extends Annotation> annotationType,
|
||||
String methodName, Class<?> expectedType) {
|
||||
Assert.notNull(targetObject, "targetObject must not be null");
|
||||
this.expectedType = expectedType;
|
||||
this.targetObject = targetObject;
|
||||
this.requiresReply = expectedType != null;
|
||||
Map<String, Map<Class<?>, HandlerMethod>> handlerMethodsForTarget = this.findHandlerMethodsForTarget(
|
||||
targetObject, annotationType, methodName, requiresReply);
|
||||
Map<Class<?>, HandlerMethod> handlerMethods = handlerMethodsForTarget.get(CANDIDATE_METHODS);
|
||||
Map<Class<?>, HandlerMethod> handlerMessageMethods = handlerMethodsForTarget.get(CANDIDATE_MESSAGE_METHODS);
|
||||
if ((handlerMethods.size() == 1 && handlerMessageMethods.isEmpty())
|
||||
|| (handlerMessageMethods.size() == 1 && handlerMethods.isEmpty())) {
|
||||
if (handlerMethods.size() == 1) {
|
||||
this.handlerMethod = handlerMethods.values().iterator().next();
|
||||
} else {
|
||||
this.handlerMethod = handlerMessageMethods.values().iterator().next();
|
||||
}
|
||||
this.handlerMethods = null;
|
||||
this.handlerMessageMethods = null;
|
||||
this.handlerMethodsList = null;
|
||||
} else {
|
||||
this.handlerMethod = null;
|
||||
this.handlerMethods = handlerMethods;
|
||||
this.handlerMessageMethods = handlerMessageMethods;
|
||||
this.handlerMethodsList = new LinkedList<Map<Class<?>, HandlerMethod>>();
|
||||
|
||||
// TODO Consider to use global option to determine a precedence of
|
||||
// methods
|
||||
this.handlerMethodsList.add(this.handlerMethods);
|
||||
this.handlerMethodsList.add(this.handlerMessageMethods);
|
||||
}
|
||||
this.prepareEvaluationContext(this.getEvaluationContext(false), methodName, annotationType);
|
||||
this.setDisplayString(targetObject, methodName);
|
||||
}
|
||||
|
||||
private void setDisplayString(Object targetObject, Object targetMethod) {
|
||||
StringBuilder sb = new StringBuilder(targetObject.getClass().getName());
|
||||
if (targetMethod instanceof Method) {
|
||||
sb.append("." + ((Method) targetMethod).getName());
|
||||
} else if (targetMethod instanceof String) {
|
||||
sb.append("." + targetMethod);
|
||||
}
|
||||
this.displayString = sb.toString() + "]";
|
||||
}
|
||||
|
||||
private void prepareEvaluationContext(StandardEvaluationContext context, Object method,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
Class<?> targetType = AopUtils.getTargetClass(this.targetObject);
|
||||
if (method instanceof Method) {
|
||||
context.registerMethodFilter(targetType, new FixedMethodFilter((Method) method));
|
||||
if (expectedType != null) {
|
||||
Assert.state(
|
||||
context.getTypeConverter().canConvert(
|
||||
TypeDescriptor.valueOf(((Method) method).getReturnType()),
|
||||
TypeDescriptor.valueOf(expectedType)), "Cannot convert to expected type ("
|
||||
+ expectedType + ") from " + method);
|
||||
}
|
||||
} else if (method == null || method instanceof String) {
|
||||
AnnotatedMethodFilter filter = new AnnotatedMethodFilter(annotationType, (String) method,
|
||||
this.requiresReply);
|
||||
Assert.state(canReturnExpectedType(filter, targetType, context.getTypeConverter()),
|
||||
"Cannot convert to expected type (" + expectedType + ") from " + method);
|
||||
context.registerMethodFilter(targetType, filter);
|
||||
}
|
||||
context.setVariable("target", targetObject);
|
||||
}
|
||||
|
||||
private boolean canReturnExpectedType(AnnotatedMethodFilter filter, Class<?> targetType, TypeConverter typeConverter) {
|
||||
if (expectedType == null) {
|
||||
return true;
|
||||
}
|
||||
List<Method> methods = filter.filter(Arrays.asList(ReflectionUtils.getAllDeclaredMethods(targetType)));
|
||||
for (Method method : methods) {
|
||||
if (typeConverter.canConvert(TypeDescriptor.valueOf(method.getReturnType()), TypeDescriptor.valueOf(expectedType))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private T processInternal(ParametersWrapper parameters) throws Exception {
|
||||
HandlerMethod candidate = this.findHandlerMethodForParameters(parameters);
|
||||
Assert.notNull(candidate, "No candidate methods found for messages.");
|
||||
Expression expression = candidate.getExpression();
|
||||
Class<?> expectedType = this.expectedType != null ? this.expectedType : candidate.method.getReturnType();
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) this.evaluateExpression(expression, parameters, expectedType);
|
||||
if (this.requiresReply) {
|
||||
Assert.notNull(result, "Expression evaluation result was null, but this processor requires a reply.");
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
Throwable evaluationException = e;
|
||||
if (e instanceof EvaluationException && e.getCause() != null) {
|
||||
evaluationException = e.getCause();
|
||||
}
|
||||
if (evaluationException instanceof Exception) {
|
||||
throw (Exception) evaluationException;
|
||||
} else {
|
||||
throw new IllegalStateException("Cannot process message", evaluationException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Map<Class<?>, HandlerMethod>> findHandlerMethodsForTarget(final Object targetObject,
|
||||
final Class<? extends Annotation> annotationType, final String methodName, final boolean requiresReply) {
|
||||
|
||||
Map<String, Map<Class<?>, HandlerMethod>> handlerMethods = new HashMap<String, Map<Class<?>, HandlerMethod>>();
|
||||
|
||||
final Map<Class<?>, HandlerMethod> candidateMethods = new HashMap<Class<?>, HandlerMethod>();
|
||||
final Map<Class<?>, HandlerMethod> candidateMessageMethods = new HashMap<Class<?>, HandlerMethod>();
|
||||
final Class<?> targetClass = this.getTargetClass(targetObject);
|
||||
MethodFilter methodFilter = new UniqueMethodFilter(targetClass);
|
||||
ReflectionUtils.doWithMethods(targetClass, new MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
boolean matchesAnnotation = false;
|
||||
if (method.isBridge()) {
|
||||
return;
|
||||
}
|
||||
if (isMethodDefinedOnObjectClass(method)) {
|
||||
return;
|
||||
}
|
||||
if (method.getDeclaringClass().equals(Proxy.class)) {
|
||||
return;
|
||||
}
|
||||
if (!Modifier.isPublic(method.getModifiers())) {
|
||||
return;
|
||||
}
|
||||
if (requiresReply && void.class.equals(method.getReturnType())) {
|
||||
return;
|
||||
}
|
||||
if (methodName != null && !methodName.equals(method.getName())) {
|
||||
return;
|
||||
}
|
||||
if (annotationType != null && AnnotationUtils.findAnnotation(method, annotationType) != null) {
|
||||
matchesAnnotation = true;
|
||||
}
|
||||
HandlerMethod handlerMethod = null;
|
||||
try {
|
||||
handlerMethod = new HandlerMethod(method);
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Method [" + method + "] is not eligible for container handling.", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Class<?> targetParameterType = handlerMethod.getTargetParameterType();
|
||||
if (matchesAnnotation || annotationType == null) {
|
||||
if (handlerMethod.isMessageMethod()) {
|
||||
if (candidateMessageMethods.containsKey(targetParameterType)) {
|
||||
throw new IllegalArgumentException("Found more than one method match for type " +
|
||||
"[Message<" + targetParameterType + ">]");
|
||||
}
|
||||
candidateMessageMethods.put(targetParameterType, handlerMethod);
|
||||
} else {
|
||||
if (candidateMethods.containsKey(targetParameterType)) {
|
||||
String exceptionMessage = "Found more than one method match for ";
|
||||
if (Void.class.equals(targetParameterType)) {
|
||||
exceptionMessage += "empty parameter for 'payload'";
|
||||
} else {
|
||||
exceptionMessage += "type [" + targetParameterType + "]";
|
||||
}
|
||||
throw new IllegalArgumentException(exceptionMessage);
|
||||
}
|
||||
candidateMethods.put(targetParameterType, handlerMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, methodFilter);
|
||||
|
||||
if (!candidateMethods.isEmpty() || !candidateMessageMethods.isEmpty()) {
|
||||
handlerMethods.put(CANDIDATE_METHODS, candidateMethods);
|
||||
handlerMethods.put(CANDIDATE_MESSAGE_METHODS, candidateMessageMethods);
|
||||
return handlerMethods;
|
||||
}
|
||||
|
||||
Assert.state(!handlerMethods.isEmpty(), "Target object of type [" + this.targetObject.getClass()
|
||||
+ "] has no eligible methods for handling Container.");
|
||||
|
||||
return handlerMethods;
|
||||
}
|
||||
|
||||
private Class<?> getTargetClass(Object targetObject) {
|
||||
Class<?> targetClass = targetObject.getClass();
|
||||
if (AopUtils.isAopProxy(targetObject)) {
|
||||
targetClass = AopUtils.getTargetClass(targetObject);
|
||||
if (targetClass == targetObject.getClass()) {
|
||||
try {
|
||||
// Maybe a proxy with no target - e.g. gateway
|
||||
Class<?>[] interfaces = ((Advised) targetObject).getProxiedInterfaces();
|
||||
if (interfaces != null && interfaces.length == 1) {
|
||||
targetClass = interfaces[0];
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Exception trying to extract interface", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (org.springframework.util.ClassUtils.isCglibProxyClass(targetClass)) {
|
||||
Class<?> superClass = targetObject.getClass().getSuperclass();
|
||||
if (!Object.class.equals(superClass)) {
|
||||
targetClass = superClass;
|
||||
}
|
||||
}
|
||||
return targetClass;
|
||||
}
|
||||
|
||||
private HandlerMethod findHandlerMethodForParameters(ParametersWrapper parameters) {
|
||||
if (this.handlerMethod != null) {
|
||||
return this.handlerMethod;
|
||||
} else {
|
||||
return this.handlerMethods.get(Void.class);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isMethodDefinedOnObjectClass(Method method) {
|
||||
if (method == null) {
|
||||
return false;
|
||||
}
|
||||
if (method.getDeclaringClass().equals(Object.class)) {
|
||||
return true;
|
||||
}
|
||||
if (ReflectionUtils.isEqualsMethod(method) || ReflectionUtils.isHashCodeMethod(method)
|
||||
|| ReflectionUtils.isToStringMethod(method) || AopUtils.isFinalizeMethod(method)) {
|
||||
return true;
|
||||
}
|
||||
return (method.getName().equals("clone") && method.getParameterTypes().length == 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper class for generating and exposing metadata for a candidate handler method. The metadata includes the SpEL
|
||||
* expression and the expected payload type.
|
||||
*/
|
||||
private static class HandlerMethod {
|
||||
|
||||
private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
|
||||
|
||||
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Expression expression;
|
||||
|
||||
private volatile TypeDescriptor targetParameterTypeDescriptor;
|
||||
|
||||
private volatile Class<?> targetParameterType = Void.class;
|
||||
|
||||
private volatile boolean messageMethod;
|
||||
|
||||
HandlerMethod(Method method) {
|
||||
this.method = method;
|
||||
this.expression = this.generateExpression(method);
|
||||
}
|
||||
|
||||
|
||||
Expression getExpression() {
|
||||
return this.expression;
|
||||
}
|
||||
|
||||
Class<?> getTargetParameterType() {
|
||||
return this.targetParameterType;
|
||||
}
|
||||
|
||||
private boolean isMessageMethod() {
|
||||
return messageMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.method.toString();
|
||||
}
|
||||
|
||||
private Expression generateExpression(Method method) {
|
||||
StringBuilder sb = new StringBuilder("#target." + method.getName() + "(");
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||
boolean hasUnqualifiedMapParameter = false;
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
if (i != 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
MethodParameter methodParameter = new MethodParameter(method, i);
|
||||
TypeDescriptor parameterTypeDescriptor = new TypeDescriptor(methodParameter);
|
||||
Class<?> parameterType = parameterTypeDescriptor.getObjectType();
|
||||
Annotation mappingAnnotation = findMappingAnnotation(parameterAnnotations[i]);
|
||||
if (mappingAnnotation != null) {
|
||||
Class<? extends Annotation> annotationType = mappingAnnotation.annotationType();
|
||||
|
||||
// if (annotationType.equals(YarnEnvironments.class)) {
|
||||
// sb.append("environment");
|
||||
// } else if (annotationType.equals(YarnEnvironment.class)) {
|
||||
// YarnEnvironment headerAnnotation = (YarnEnvironment) mappingAnnotation;
|
||||
// sb.append(this.determineEnvironmentExpression(headerAnnotation, methodParameter));
|
||||
// } else if (annotationType.equals(YarnParameters.class)) {
|
||||
// Assert.isTrue(Map.class.isAssignableFrom(parameterType),
|
||||
// "The @YarnParameters annotation can only be applied to a Map-typed parameter.");
|
||||
// sb.append("parameters");
|
||||
// } else if (annotationType.equals(YarnParameter.class)) {
|
||||
// YarnParameter headerAnnotation = (YarnParameter) mappingAnnotation;
|
||||
// sb.append(this.determineParameterExpression(headerAnnotation, methodParameter));
|
||||
// }
|
||||
}
|
||||
}
|
||||
if (hasUnqualifiedMapParameter) {
|
||||
if (targetParameterType != null && Map.class.isAssignableFrom(this.targetParameterType)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to determine payload matching parameter due to ambiguous Map typed parameters. "
|
||||
+ "Consider adding the @Payload and or @Headers annotations as appropriate.");
|
||||
}
|
||||
}
|
||||
sb.append(")");
|
||||
if (this.targetParameterTypeDescriptor == null) {
|
||||
this.targetParameterTypeDescriptor = TypeDescriptor.valueOf(Void.class);
|
||||
}
|
||||
return EXPRESSION_PARSER.parseExpression(sb.toString());
|
||||
}
|
||||
|
||||
private Annotation findMappingAnnotation(Annotation[] annotations) {
|
||||
if (annotations == null || annotations.length == 0) {
|
||||
return null;
|
||||
}
|
||||
Annotation match = null;
|
||||
for (Annotation annotation : annotations) {
|
||||
Class<? extends Annotation> type = annotation.annotationType();
|
||||
// if (type.equals(YarnParameters.class) || type.equals(YarnParameter.class)
|
||||
// || type.equals(YarnEnvironments.class) || type.equals(YarnEnvironment.class)) {
|
||||
// if (match != null) {
|
||||
// throw new IllegalArgumentException(
|
||||
// "At most one parameter annotation can be provided for message mapping, "
|
||||
// + "but found two: [" + match.annotationType().getName() + "] and ["
|
||||
// + annotation.annotationType().getName() + "]");
|
||||
// }
|
||||
// match = annotation;
|
||||
// }
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
// private String determineParameterExpression(YarnParameter parameterAnnotation, MethodParameter methodParameter) {
|
||||
// methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
|
||||
// String headerName = null;
|
||||
// String relativeExpression = "";
|
||||
// String valueAttribute = parameterAnnotation.value();
|
||||
// if (!StringUtils.hasText(valueAttribute)) {
|
||||
// headerName = methodParameter.getParameterName();
|
||||
// } else if (valueAttribute.indexOf('.') != -1) {
|
||||
// String tokens[] = valueAttribute.split("\\.", 2);
|
||||
// headerName = tokens[0];
|
||||
// if (StringUtils.hasText(tokens[1])) {
|
||||
// relativeExpression = "." + tokens[1];
|
||||
// }
|
||||
// } else {
|
||||
// headerName = valueAttribute;
|
||||
// }
|
||||
// Assert.notNull(headerName, "Cannot determine parameter name. Possible reasons: -debug is "
|
||||
// + "disabled or header name is not explicitly provided via @YarnParameter annotation.");
|
||||
// String headerRetrievalExpression = "parameters['" + headerName + "']";
|
||||
// String fullHeaderExpression = headerRetrievalExpression + relativeExpression;
|
||||
// String fallbackExpression = (parameterAnnotation.required()) ? "T(org.springframework.util.Assert).isTrue(false, 'required parameter not available: "
|
||||
// + headerName + "')"
|
||||
// : "null";
|
||||
// return headerRetrievalExpression + " != null ? " + fullHeaderExpression + " : " + fallbackExpression;
|
||||
// }
|
||||
|
||||
// private String determineEnvironmentExpression(YarnEnvironment environmentAnnotation, MethodParameter methodParameter) {
|
||||
// methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
|
||||
// String headerName = null;
|
||||
// String relativeExpression = "";
|
||||
// String valueAttribute = environmentAnnotation.value();
|
||||
// if (!StringUtils.hasText(valueAttribute)) {
|
||||
// headerName = methodParameter.getParameterName();
|
||||
// } else if (valueAttribute.indexOf('.') != -1) {
|
||||
// String tokens[] = valueAttribute.split("\\.", 2);
|
||||
// headerName = tokens[0];
|
||||
// if (StringUtils.hasText(tokens[1])) {
|
||||
// relativeExpression = "." + tokens[1];
|
||||
// }
|
||||
// } else {
|
||||
// headerName = valueAttribute;
|
||||
// }
|
||||
// Assert.notNull(headerName, "Cannot determine parameter name. Possible reasons: -debug is "
|
||||
// + "disabled or header name is not explicitly provided via @YarnEnvironment annotation.");
|
||||
// String headerRetrievalExpression = "environment['" + headerName + "']";
|
||||
// String fullHeaderExpression = headerRetrievalExpression + relativeExpression;
|
||||
// String fallbackExpression = (environmentAnnotation.required()) ? "T(org.springframework.util.Assert).isTrue(false, 'required parameter not available: "
|
||||
// + headerName + "')"
|
||||
// : "null";
|
||||
// return headerRetrievalExpression + " != null ? " + fullHeaderExpression + " : " + fallbackExpression;
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping everything we need to work with spel.
|
||||
*/
|
||||
public class ParametersWrapper {
|
||||
|
||||
private final StateContext stateContext;
|
||||
|
||||
public ParametersWrapper(StateContext stateContext) {
|
||||
this.stateContext = stateContext;
|
||||
}
|
||||
|
||||
public StateContext getStateContext() {
|
||||
return stateContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
|
||||
public class StateMachineOnTransitionHandler extends StateMachineHandler {
|
||||
|
||||
private OnTransition annotation;
|
||||
|
||||
public StateMachineOnTransitionHandler(Object target, Method method, OnTransition annotation) {
|
||||
super(target, method);
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
public OnTransition getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import org.springframework.statemachine.StateContext;
|
||||
|
||||
/**
|
||||
* A generic runtime representation of a state machine.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public interface StateMachineRuntime {
|
||||
|
||||
StateContext getStateContext();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
/**
|
||||
* Defines a strategy of processing a state machine and returning
|
||||
* some Object (or null).
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <T> type
|
||||
*/
|
||||
public interface StateMachineRuntimeProcessor<T> {
|
||||
|
||||
/**
|
||||
* Process the container based on information available
|
||||
* from {@link StateMachineRuntime}.
|
||||
*
|
||||
* @param stateMachineRuntime the yarn container runtime
|
||||
* @return the result
|
||||
*/
|
||||
T process(StateMachineRuntime stateMachineRuntime);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.state;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
|
||||
public abstract class AbstractState<S, E> implements State<S, E> {
|
||||
|
||||
private S id;
|
||||
private Collection<E> deferred;
|
||||
private Collection<Action> entryActions;
|
||||
private Collection<Action> exitActions;
|
||||
|
||||
public AbstractState(S id) {
|
||||
this(id, null);
|
||||
}
|
||||
|
||||
public AbstractState(S id, Collection<E> deferred) {
|
||||
this(id, deferred, null, null);
|
||||
}
|
||||
|
||||
public AbstractState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions) {
|
||||
this.id = id;
|
||||
this.deferred = deferred;
|
||||
this.entryActions = entryActions;
|
||||
this.exitActions = exitActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public S getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<E> getDeferredEvents() {
|
||||
return deferred;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Action> getEntryActions() {
|
||||
return entryActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Action> getExitActions() {
|
||||
return exitActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AbstractState [id=" + id + ", deferred=" + deferred + ", entryActions=" + entryActions
|
||||
+ ", exitActions=" + exitActions + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.state;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
|
||||
public class EnumState<S extends Enum<S>, E extends Enum<E>> extends AbstractState<S, E> {
|
||||
|
||||
public EnumState(S id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
public EnumState(S id, Collection<E> deferred) {
|
||||
super(id, deferred);
|
||||
}
|
||||
|
||||
public EnumState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions) {
|
||||
super(id, deferred, entryActions, exitActions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EnumState [getId()=" + getId() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode()
|
||||
+ ", toString()=" + super.toString() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.state;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
|
||||
/**
|
||||
* {@code State} is an interface representing possible state in a state machine.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public interface State<S, E> {
|
||||
|
||||
/**
|
||||
* Gets the state identifier.
|
||||
*
|
||||
* @return the identifier
|
||||
*/
|
||||
S getId();
|
||||
|
||||
/**
|
||||
* Gets the deferred events for this state.
|
||||
*
|
||||
* @return the state deferred events
|
||||
*/
|
||||
Collection<E> getDeferredEvents();
|
||||
|
||||
/**
|
||||
* Gets {@link Action}s executed entering in this state.
|
||||
*
|
||||
* @return the state entry actions
|
||||
*/
|
||||
Collection<Action> getEntryActions();
|
||||
|
||||
/**
|
||||
* Gets {@link Action}s executed exiting from this state.
|
||||
*
|
||||
* @return the state exit actions
|
||||
*/
|
||||
Collection<Action> getExitActions();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
/**
|
||||
* Base class providing common functionality for using Spring expression language.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Dave Syer
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractExpressionEvaluator implements BeanFactoryAware, InitializingBean {
|
||||
|
||||
private volatile StandardEvaluationContext evaluationContext;
|
||||
|
||||
private final ExpressionParser expressionParser = new SpelExpressionParser();
|
||||
|
||||
private final BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter();
|
||||
|
||||
private volatile BeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(final BeanFactory beanFactory) {
|
||||
if (beanFactory != null) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.typeConverter.setBeanFactory(beanFactory);
|
||||
if (this.evaluationContext != null && this.evaluationContext.getBeanResolver() == null) {
|
||||
this.evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
getEvaluationContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the conversion service.
|
||||
*
|
||||
* @param conversionService the new conversion service
|
||||
*/
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
if (conversionService != null) {
|
||||
this.typeConverter.setConversionService(conversionService);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the evaluation context.
|
||||
*
|
||||
* @return the evaluation context
|
||||
*/
|
||||
protected StandardEvaluationContext getEvaluationContext() {
|
||||
return this.getEvaluationContext(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a WARN log if the beanFactory field is null, unless the argument is false.
|
||||
* @param beanFactoryRequired set to false to suppress the warning.
|
||||
* @return The evaluation context.
|
||||
*/
|
||||
protected final StandardEvaluationContext getEvaluationContext(boolean beanFactoryRequired) {
|
||||
if (this.evaluationContext == null) {
|
||||
if (this.beanFactory == null && !beanFactoryRequired) {
|
||||
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext();
|
||||
}
|
||||
else {
|
||||
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.beanFactory);
|
||||
}
|
||||
if (this.typeConverter != null) {
|
||||
this.evaluationContext.setTypeConverter(this.typeConverter);
|
||||
}
|
||||
}
|
||||
return this.evaluationContext;
|
||||
}
|
||||
|
||||
protected Object evaluateExpression(String expression, Object input) {
|
||||
return this.evaluateExpression(expression, input, (Class<?>) null);
|
||||
}
|
||||
|
||||
protected <T> T evaluateExpression(String expression, Object input, Class<T> expectedType) {
|
||||
return this.expressionParser.parseExpression(expression).getValue(this.getEvaluationContext(), input, expectedType);
|
||||
}
|
||||
|
||||
protected Object evaluateExpression(Expression expression, Object input) {
|
||||
return this.evaluateExpression(expression, input, (Class<?>) null);
|
||||
}
|
||||
|
||||
protected <T> T evaluateExpression(Expression expression, Class<T> expectedType) {
|
||||
return expression.getValue(this.getEvaluationContext(), expectedType);
|
||||
}
|
||||
|
||||
protected Object evaluateExpression(Expression expression) {
|
||||
return expression.getValue(this.getEvaluationContext());
|
||||
}
|
||||
|
||||
protected <T> T evaluateExpression(Expression expression, Object input, Class<T> expectedType) {
|
||||
return expression.getValue(this.getEvaluationContext(), input, expectedType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
import org.springframework.statemachine.listener.CompositeStateMachineListener;
|
||||
import org.springframework.statemachine.listener.StateMachineListener;
|
||||
import org.springframework.statemachine.processor.StateMachineHandler;
|
||||
import org.springframework.statemachine.processor.StateMachineOnTransitionHandler;
|
||||
import org.springframework.statemachine.processor.StateMachineRuntime;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
import org.springframework.statemachine.transition.TransitionKind;
|
||||
import org.springframework.statemachine.trigger.Trigger;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base implementation of a {@link StateMachine} loosely modelled from UML state
|
||||
* machine.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport implements StateMachine<State<S,E>, E> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(AbstractStateMachine.class);
|
||||
|
||||
private final Collection<State<S,E>> states;
|
||||
|
||||
private final Collection<Transition<S,E>> transitions;
|
||||
|
||||
private final State<S,E> initialState;
|
||||
|
||||
private final ExtendedState extendedState;
|
||||
|
||||
private final Queue<Message<E>> eventQueue = new ConcurrentLinkedQueue<Message<E>>();
|
||||
|
||||
private final LinkedList<Message<E>> deferList = new LinkedList<Message<E>>();
|
||||
|
||||
private final CompositeStateMachineListener<S, E> stateListener = new CompositeStateMachineListener<S, E>();
|
||||
|
||||
private volatile State<S,E> currentState;
|
||||
|
||||
private volatile Runnable task;
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state machine.
|
||||
*
|
||||
* @param states the states of this machine
|
||||
* @param transitions the transitions of this machine
|
||||
* @param initialState the initial state of this machine
|
||||
*/
|
||||
public AbstractStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
|
||||
State<S, E> initialState) {
|
||||
this(states, transitions, initialState, new DefaultExtendedState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state machine.
|
||||
*
|
||||
* @param states the states of this machine
|
||||
* @param transitions the transitions of this machine
|
||||
* @param initialState the initial state of this machine
|
||||
* @param extendedState the extended state of this machine
|
||||
*/
|
||||
public AbstractStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
|
||||
State<S, E> initialState, ExtendedState extendedState) {
|
||||
super();
|
||||
this.states = states;
|
||||
this.transitions = transitions;
|
||||
this.initialState = initialState;
|
||||
this.extendedState = extendedState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State<S,E> getState() {
|
||||
return currentState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State<S,E> getInitialState() {
|
||||
return initialState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEvent(Message<E> event) {
|
||||
// TODO: machine header looks weird!
|
||||
event = MessageBuilder.fromMessage(event).setHeader("machine", this).build();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Queue event " + event);
|
||||
}
|
||||
eventQueue.add(event);
|
||||
scheduleEventQueueProcessing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEvent(E event) {
|
||||
sendEvent(MessageBuilder.withPayload(event).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() {
|
||||
super.doStart();
|
||||
switchToState(initialState, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addStateListener(StateMachineListener<State<S, E>, E> listener) {
|
||||
stateListener.register(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link State}s defined in this machine. Returned collection is
|
||||
* an unmodifiable copy because states in a state machine are immutable.
|
||||
*
|
||||
* @return immutable copy of existing states
|
||||
*/
|
||||
public Collection<State<S,E>> getStates() {
|
||||
return Collections.unmodifiableCollection(states);
|
||||
}
|
||||
|
||||
private void switchToState(State<S,E> state, Message<E> event) {
|
||||
log.info("Moving into state=" + state + " from " + currentState);
|
||||
|
||||
exitFromState(currentState, event);
|
||||
stateListener.stateChanged(currentState, state);
|
||||
|
||||
callHandlers(currentState, state, event);
|
||||
|
||||
currentState = state;
|
||||
entryToState(state, event);
|
||||
|
||||
for (Transition<S,E> transition : transitions) {
|
||||
State<S,E> source = transition.getSource();
|
||||
State<S,E> target = transition.getTarget();
|
||||
if (transition.getTrigger() == null && source.equals(currentState)) {
|
||||
switchToState(target, event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void exitFromState(State<S, E> state, Message<E> event) {
|
||||
if (state != null) {
|
||||
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
Collection<Action> actions = state.getExitActions();
|
||||
if (actions != null) {
|
||||
for (Action action : actions) {
|
||||
action.execute(new DefaultStateContext(messageHeaders, extendedState));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void entryToState(State<S,E> state, Message<E> event) {
|
||||
if (state != null) {
|
||||
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
Collection<Action> actions = state.getEntryActions();
|
||||
if (actions != null) {
|
||||
for (Action action : actions) {
|
||||
action.execute(new DefaultStateContext(messageHeaders, extendedState));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processEventQueue() {
|
||||
log.debug("Process event queue");
|
||||
Message<E> queuedEvent = null;
|
||||
while ((queuedEvent = eventQueue.poll()) != null) {
|
||||
Message<E> defer = null;
|
||||
for (Transition<S,E> transition : transitions) {
|
||||
State<S,E> source = transition.getSource();
|
||||
State<S,E> target = transition.getTarget();
|
||||
Trigger<S, E> trigger = transition.getTrigger();
|
||||
if (source.equals(currentState)) {
|
||||
if (trigger != null && trigger.evaluate(queuedEvent.getPayload())) {
|
||||
boolean transit = transition.transit(new DefaultStateContext(queuedEvent.getHeaders(), extendedState));
|
||||
if (transit && transition.getKind() != TransitionKind.INTERNAL) {
|
||||
switchToState(target, queuedEvent);
|
||||
}
|
||||
break;
|
||||
} else if (source.getDeferredEvents() != null && source.getDeferredEvents().contains(queuedEvent.getPayload())) {
|
||||
defer = queuedEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (defer != null) {
|
||||
log.info("Deferring event " + defer);
|
||||
deferList.addLast(defer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processDeferList() {
|
||||
log.debug("Process defer list");
|
||||
ListIterator<Message<E>> iterator = deferList.listIterator();
|
||||
while (iterator.hasNext()) {
|
||||
Message<E> event = iterator.next();
|
||||
for (Transition<S,E> transition : transitions) {
|
||||
State<S,E> source = transition.getSource();
|
||||
State<S,E> target = transition.getTarget();
|
||||
Trigger<S, E> trigger = transition.getTrigger();
|
||||
if (source.equals(currentState)) {
|
||||
if (trigger != null && trigger.evaluate(event.getPayload())) {
|
||||
boolean transit = transition.transit(new DefaultStateContext(event.getHeaders(), extendedState));
|
||||
if (transit && transition.getKind() != TransitionKind.INTERNAL) {
|
||||
switchToState(target, event);
|
||||
}
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleEventQueueProcessing() {
|
||||
if (task == null) {
|
||||
task = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
processEventQueue();
|
||||
processDeferList();
|
||||
task = null;
|
||||
}
|
||||
};
|
||||
getTaskExecutor().execute(task);
|
||||
}
|
||||
}
|
||||
|
||||
private void callHandlers(State<S,E> sourceState, State<S,E> targetState, Message<E> event) {
|
||||
if (sourceState != null && targetState != null) {
|
||||
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
StateContext stateContext = new DefaultStateContext(messageHeaders, extendedState);
|
||||
getStateMachineHandlerResults(getStateMachineHandlers(sourceState, targetState), stateContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private List<Object> getStateMachineHandlerResults(List<StateMachineHandler> stateMachineHandlers, final StateContext stateContext) {
|
||||
StateMachineRuntime runtime = new StateMachineRuntime() {
|
||||
@Override
|
||||
public StateContext getStateContext() {
|
||||
return stateContext;
|
||||
}
|
||||
};
|
||||
List<Object> results = new ArrayList<Object>();
|
||||
for (StateMachineHandler handler : stateMachineHandlers) {
|
||||
results.add(handler.handle(runtime));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<StateMachineHandler> getStateMachineHandlers(State<S,E> sourceState, State<S,E> targetState) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
|
||||
// TODO think how to handle null bf
|
||||
if (beanFactory == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Assert.state(beanFactory instanceof ListableBeanFactory, "Bean factory must be instance of ListableBeanFactory");
|
||||
Map<String, StateMachineOnTransitionHandler> handlers = ((ListableBeanFactory) beanFactory)
|
||||
.getBeansOfType(StateMachineOnTransitionHandler.class);
|
||||
List<StateMachineHandler> handlersList = new ArrayList<StateMachineHandler>();
|
||||
|
||||
for (Entry<String, StateMachineOnTransitionHandler> entry : handlers.entrySet()) {
|
||||
OnTransition annotation = entry.getValue().getAnnotation();
|
||||
String source = annotation.source();
|
||||
String target = annotation.target();
|
||||
String s = sourceState.getId().toString();
|
||||
String t = targetState.getId().toString();
|
||||
if (s.equals(source) && t.equals(target)) {
|
||||
handlersList.add(entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
OrderComparator comparator = new OrderComparator();
|
||||
Collections.sort(handlersList, comparator);
|
||||
return handlersList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.expression.MethodFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A MethodFilter implementation that enables the following:
|
||||
* <ol>
|
||||
* <li>matching on method name, if available</li>
|
||||
* <li>exclusion of void-returning methods if 'requiresReply' is true</li>
|
||||
* <li>limiting to annotated methods if at least one is present</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class AnnotatedMethodFilter implements MethodFilter {
|
||||
|
||||
private final Class<? extends Annotation> annotationType;
|
||||
|
||||
private final String methodName;
|
||||
|
||||
private final boolean requiresReply;
|
||||
|
||||
public AnnotatedMethodFilter(Class<? extends Annotation> annotationType, String methodName, boolean requiresReply) {
|
||||
this.annotationType = annotationType;
|
||||
this.methodName = methodName;
|
||||
this.requiresReply = requiresReply;
|
||||
}
|
||||
|
||||
public List<Method> filter(List<Method> methods) {
|
||||
List<Method> annotatedCandidates = new ArrayList<Method>();
|
||||
List<Method> fallbackCandidates = new ArrayList<Method>();
|
||||
for (Method method : methods) {
|
||||
if (method.isBridge()) {
|
||||
continue;
|
||||
}
|
||||
if (this.requiresReply && method.getReturnType().equals(void.class)) {
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.hasText(this.methodName) && !this.methodName.equals(method.getName())) {
|
||||
continue;
|
||||
}
|
||||
if (this.annotationType != null && AnnotationUtils.findAnnotation(method, this.annotationType) != null) {
|
||||
annotatedCandidates.add(method);
|
||||
} else {
|
||||
fallbackCandidates.add(method);
|
||||
}
|
||||
}
|
||||
return (!annotatedCandidates.isEmpty()) ? annotatedCandidates : fallbackCandidates;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.beans.PropertyEditor;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.SimpleTypeConverter;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Soby Chacko
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class BeanFactoryTypeConverter implements TypeConverter, BeanFactoryAware {
|
||||
|
||||
private static ConversionService defaultConversionService;
|
||||
|
||||
private volatile SimpleTypeConverter delegate = new SimpleTypeConverter();
|
||||
|
||||
private volatile boolean haveCalledDelegateGetDefaultEditor;
|
||||
|
||||
private volatile ConversionService conversionService;
|
||||
|
||||
/**
|
||||
* Instantiates a new bean factory type converter.
|
||||
*/
|
||||
public BeanFactoryTypeConverter() {
|
||||
synchronized (BeanFactoryTypeConverter.class) {
|
||||
if (defaultConversionService == null) {
|
||||
defaultConversionService = new DefaultConversionService();
|
||||
}
|
||||
}
|
||||
this.conversionService = defaultConversionService;
|
||||
}
|
||||
|
||||
public BeanFactoryTypeConverter(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
if (beanFactory instanceof ConfigurableBeanFactory) {
|
||||
Object typeConverter = ((ConfigurableBeanFactory) beanFactory).getTypeConverter();
|
||||
if (typeConverter instanceof SimpleTypeConverter) {
|
||||
delegate = (SimpleTypeConverter) typeConverter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
|
||||
if (conversionService.canConvert(sourceType, targetType)) {
|
||||
return true;
|
||||
}
|
||||
if (!String.class.isAssignableFrom(sourceType) && !String.class.isAssignableFrom(targetType)) {
|
||||
// PropertyEditor cannot convert non-Strings
|
||||
return false;
|
||||
}
|
||||
if (!String.class.isAssignableFrom(sourceType)) {
|
||||
return delegate.findCustomEditor(sourceType, null) != null || this.getDefaultEditor(sourceType) != null;
|
||||
}
|
||||
return delegate.findCustomEditor(targetType, null) != null || this.getDefaultEditor(targetType) != null;
|
||||
}
|
||||
|
||||
public boolean canConvert(TypeDescriptor sourceTypeDescriptor, TypeDescriptor targetTypeDescriptor) {
|
||||
if (conversionService.canConvert(sourceTypeDescriptor, targetTypeDescriptor)) {
|
||||
return true;
|
||||
}
|
||||
// TODO: what does this mean? This method is not used in SpEL so
|
||||
// probably ignorable?
|
||||
Class<?> sourceType = sourceTypeDescriptor.getObjectType();
|
||||
Class<?> targetType = targetTypeDescriptor.getObjectType();
|
||||
return canConvert(sourceType, targetType);
|
||||
}
|
||||
|
||||
public Object convertValue(Object value, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
// Echoes
|
||||
// org.springframework.expression.common.ExpressionUtils.convertTypedValue()
|
||||
if ((targetType.getType() == Void.class || targetType.getType() == Void.TYPE) && value == null) {
|
||||
return null;
|
||||
}
|
||||
if (sourceType != null) {
|
||||
Class<?> sourceClass = sourceType.getType();
|
||||
//Class<?> targetClass = targetType.getType();
|
||||
if (sourceType.isAssignableTo(targetType) && ClassUtils.isPrimitiveArray(sourceClass)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
if (conversionService.canConvert(sourceType, targetType)) {
|
||||
return conversionService.convert(value, sourceType, targetType);
|
||||
}
|
||||
if (!String.class.isAssignableFrom(sourceType.getType())) {
|
||||
PropertyEditor editor = delegate.findCustomEditor(sourceType.getType(), null);
|
||||
if (editor == null) {
|
||||
editor = this.getDefaultEditor(sourceType.getType());
|
||||
}
|
||||
if (editor != null) { // INT-1441
|
||||
String text = null;
|
||||
synchronized (editor) {
|
||||
editor.setValue(value);
|
||||
text = editor.getAsText();
|
||||
}
|
||||
if (String.class.isAssignableFrom(targetType.getType())) {
|
||||
return text;
|
||||
}
|
||||
return convertValue(text, TypeDescriptor.valueOf(String.class), targetType);
|
||||
}
|
||||
}
|
||||
return delegate.convertIfNecessary(value, targetType.getType());
|
||||
}
|
||||
|
||||
private PropertyEditor getDefaultEditor(Class<?> sourceType) {
|
||||
PropertyEditor defaultEditor;
|
||||
if (this.haveCalledDelegateGetDefaultEditor) {
|
||||
defaultEditor = delegate.getDefaultEditor(sourceType);
|
||||
} else {
|
||||
synchronized (this) {
|
||||
// not thread-safe - it builds the defaultEditors field in-place
|
||||
// (SPR-10191)
|
||||
defaultEditor = delegate.getDefaultEditor(sourceType);
|
||||
}
|
||||
this.haveCalledDelegateGetDefaultEditor = true;
|
||||
}
|
||||
return defaultEditor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
|
||||
/**
|
||||
* Default implementation of a {@link ExtendedState}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class DefaultExtendedState implements ExtendedState {
|
||||
|
||||
private final Map<String, Object> variables;
|
||||
|
||||
/**
|
||||
* Instantiates a new default extended state.
|
||||
*/
|
||||
public DefaultExtendedState() {
|
||||
this.variables = new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getVariables() {
|
||||
return variables;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
|
||||
public class DefaultStateContext implements StateContext {
|
||||
|
||||
private final MessageHeaders messageHeaders;
|
||||
|
||||
private final ExtendedState extendedState;
|
||||
|
||||
public DefaultStateContext(MessageHeaders messageHeaders, ExtendedState extendedState) {
|
||||
this.messageHeaders = messageHeaders;
|
||||
this.extendedState = extendedState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageHeaders getMessageHeaders() {
|
||||
return messageHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtendedState getExtendedState() {
|
||||
return extendedState;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardTypeConverter;
|
||||
|
||||
/**
|
||||
* Utility class with static methods for helping with establishing environments for
|
||||
* SpEL expressions.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public abstract class ExpressionUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ExpressionUtils.class);
|
||||
|
||||
/**
|
||||
* Create a {@link StandardEvaluationContext} with a {@link MapAccessor} in its
|
||||
* property accessor property and the supplied {@link ConversionService} in its
|
||||
* conversionService property.
|
||||
*
|
||||
* @param conversionService the conversion service.
|
||||
* @return the evaluation context.
|
||||
*/
|
||||
private static StandardEvaluationContext createStandardEvaluationContext(ConversionService conversionService,
|
||||
BeanFactory beanFactory) {
|
||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
|
||||
evaluationContext.addPropertyAccessor(new MapAccessor());
|
||||
if (conversionService != null) {
|
||||
evaluationContext.setTypeConverter(new StandardTypeConverter(conversionService));
|
||||
}
|
||||
if (beanFactory != null) {
|
||||
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
|
||||
}
|
||||
return evaluationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to create a context with no BeanFactory, usually in tests.
|
||||
* @return The evaluation context.
|
||||
*/
|
||||
public static StandardEvaluationContext createStandardEvaluationContext() {
|
||||
return doCreateContext(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the context from the beanFactory if not null; emits a warning if the beanFactory
|
||||
* is null.
|
||||
* @param beanFactory The bean factory.
|
||||
* @return The evaluation context.
|
||||
*/
|
||||
public static StandardEvaluationContext createStandardEvaluationContext(BeanFactory beanFactory) {
|
||||
if (beanFactory == null) {
|
||||
logger.warn("Creating EvaluationContext with no beanFactory", new RuntimeException("No beanfactory"));
|
||||
}
|
||||
return doCreateContext(beanFactory);
|
||||
}
|
||||
|
||||
private static StandardEvaluationContext doCreateContext(BeanFactory beanFactory) {
|
||||
ConversionService conversionService = null;
|
||||
StandardEvaluationContext evaluationContext = null;
|
||||
if (beanFactory != null) {
|
||||
evaluationContext = StateMachineContextUtils.getEvaluationContext(beanFactory);
|
||||
}
|
||||
if (evaluationContext == null) {
|
||||
if (beanFactory != null) {
|
||||
conversionService = StateMachineContextUtils.getConversionService(beanFactory);
|
||||
}
|
||||
evaluationContext = createStandardEvaluationContext(conversionService, beanFactory);
|
||||
}
|
||||
return evaluationContext;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.expression.MethodFilter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link MethodFilter} implementation that will always return the same Method
|
||||
* instance within a single-element list if it is present in the candidate list.
|
||||
* If the Method is not present in the candidate list, it will return an empty
|
||||
* list.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class FixedMethodFilter implements MethodFilter {
|
||||
|
||||
private final Method method;
|
||||
|
||||
public FixedMethodFilter(Method method) {
|
||||
Assert.notNull(method, "method must not be null");
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public List<Method> filter(List<Method> methods) {
|
||||
if (methods != null && methods.contains(this.method)) {
|
||||
List<Method> filteredList = new ArrayList<Method>(1);
|
||||
filteredList.add(this.method);
|
||||
return filteredList;
|
||||
}
|
||||
return Collections.<Method> emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Convenient base class for object which needs spring task scheduler, task
|
||||
* executor and life cycle handling.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public abstract class LifecycleObjectSupport implements InitializingBean, SmartLifecycle, BeanFactoryAware {
|
||||
|
||||
private static final Log log = LogFactory.getLog(LifecycleObjectSupport.class);
|
||||
|
||||
// fields for lifecycle
|
||||
private volatile boolean autoStartup = true;
|
||||
private volatile int phase = 0;
|
||||
private volatile boolean running;
|
||||
|
||||
// lock to protect lifycycle methods
|
||||
private final ReentrantLock lifecycleLock = new ReentrantLock();
|
||||
|
||||
// common task handling
|
||||
private TaskScheduler taskScheduler;
|
||||
private TaskExecutor taskExecutor;
|
||||
|
||||
// to access bean factory
|
||||
private volatile BeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public final void afterPropertiesSet() {
|
||||
try {
|
||||
this.onInit();
|
||||
} catch (Exception e) {
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
}
|
||||
throw new BeanInitializationException("failed to initialize", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
Assert.notNull(beanFactory, "beanFactory must not be null");
|
||||
if(log.isDebugEnabled()) {
|
||||
log.debug("Setting bean factory: " + beanFactory + " for " + this);
|
||||
}
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isAutoStartup() {
|
||||
return this.autoStartup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getPhase() {
|
||||
return this.phase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isRunning() {
|
||||
this.lifecycleLock.lock();
|
||||
try {
|
||||
return this.running;
|
||||
} finally {
|
||||
this.lifecycleLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void start() {
|
||||
this.lifecycleLock.lock();
|
||||
try {
|
||||
if (!this.running) {
|
||||
this.doStart();
|
||||
this.running = true;
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("started " + this);
|
||||
} else {
|
||||
if(log.isDebugEnabled()) {
|
||||
log.debug("already started " + this);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.lifecycleLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void stop() {
|
||||
this.lifecycleLock.lock();
|
||||
try {
|
||||
if (this.running) {
|
||||
this.doStop();
|
||||
this.running = false;
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("stopped " + this);
|
||||
}
|
||||
} else {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("already stopped " + this);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.lifecycleLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void stop(Runnable callback) {
|
||||
this.lifecycleLock.lock();
|
||||
try {
|
||||
this.stop();
|
||||
callback.run();
|
||||
} finally {
|
||||
this.lifecycleLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the auto startup.
|
||||
*
|
||||
* @param autoStartup the new auto startup
|
||||
* @see SmartLifecycle
|
||||
*/
|
||||
public void setAutoStartup(boolean autoStartup) {
|
||||
this.autoStartup = autoStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the phase.
|
||||
*
|
||||
* @param phase the new phase
|
||||
* @see SmartLifecycle
|
||||
*/
|
||||
public void setPhase(int phase) {
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link BeanFactory} for this instance.
|
||||
*
|
||||
* @return the bean factory.
|
||||
*/
|
||||
protected final BeanFactory getBeanFactory() {
|
||||
return beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the used {@link TaskScheduler}.
|
||||
*
|
||||
* @param taskScheduler the task scheduler
|
||||
*/
|
||||
public void setTaskScheduler(TaskScheduler taskScheduler) {
|
||||
Assert.notNull(taskScheduler, "taskScheduler must not be null");
|
||||
this.taskScheduler = taskScheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the defined {@link TaskScheduler}.
|
||||
*
|
||||
* @return the defined task scheduler
|
||||
*/
|
||||
protected TaskScheduler getTaskScheduler() {
|
||||
if(taskScheduler == null && getBeanFactory() != null) {
|
||||
if(log.isDebugEnabled()) {
|
||||
log.debug("getting taskScheduler service from bean factory " + getBeanFactory());
|
||||
}
|
||||
taskScheduler = StateMachineContextUtils.getTaskScheduler(getBeanFactory());
|
||||
}
|
||||
return taskScheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the used {@link TaskExecutor}.
|
||||
*
|
||||
* @param taskExecutor the task executor
|
||||
*/
|
||||
public void setTaskExecutor(TaskExecutor taskExecutor) {
|
||||
Assert.notNull(taskExecutor, "taskExecutor must not be null");
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the defined {@link TaskExecutor}.
|
||||
*
|
||||
* @return the defined task executor
|
||||
*/
|
||||
protected TaskExecutor getTaskExecutor() {
|
||||
if(taskExecutor == null && getBeanFactory() != null) {
|
||||
if(log.isDebugEnabled()) {
|
||||
log.debug("getting taskExecutor service from bean factory " + getBeanFactory());
|
||||
}
|
||||
taskExecutor = StateMachineContextUtils.getTaskExecutor(getBeanFactory());
|
||||
}
|
||||
return taskExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may implement this for initialization logic. Called
|
||||
* during the {@link InitializingBean} phase. Implementor should
|
||||
* always call super method not to break initialization chain.
|
||||
*
|
||||
* @throws Exception exception
|
||||
*/
|
||||
protected void onInit() throws Exception {}
|
||||
|
||||
/**
|
||||
* Subclasses may implement this method with the start behavior. This
|
||||
* method will be invoked while holding the {@link #lifecycleLock}.
|
||||
*/
|
||||
protected void doStart() {};
|
||||
|
||||
/**
|
||||
* Subclasses may implement this method with the stop behavior. This method
|
||||
* will be invoked while holding the {@link #lifecycleLock}.
|
||||
*/
|
||||
protected void doStop() {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility methods for accessing common components from the BeanFactory.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class StateMachineContextUtils {
|
||||
|
||||
/* Default task scheduler bean name */
|
||||
public static final String TASK_SCHEDULER_BEAN_NAME = "taskScheduler";
|
||||
|
||||
/* Default task executor bean name */
|
||||
public static final String TASK_EXECUTOR_BEAN_NAME = "taskExecutor";
|
||||
|
||||
/* Default conversion service bean name */
|
||||
public static final String CONVERSION_SERVICE_BEAN_NAME = "cloudClusterConversionService";
|
||||
|
||||
/* Default evaluation context bean name */
|
||||
public static final String EVALUATION_CONTEXT_BEAN_NAME = "cloudClusterEvaluationContext";
|
||||
|
||||
/**
|
||||
* Return the {@link TaskScheduler} bean whose name is "taskScheduler" if
|
||||
* available.
|
||||
*
|
||||
* @param beanFactory BeanFactory for lookup, must not be null.
|
||||
* @return task scheduler
|
||||
*/
|
||||
public static TaskScheduler getTaskScheduler(BeanFactory beanFactory) {
|
||||
return getBeanOfType(beanFactory, TASK_SCHEDULER_BEAN_NAME, TaskScheduler.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link TaskScheduler} bean whose name is "taskExecutor" if
|
||||
* available.
|
||||
*
|
||||
* @param beanFactory BeanFactory for lookup, must not be null.
|
||||
* @return task executor
|
||||
*/
|
||||
public static TaskExecutor getTaskExecutor(BeanFactory beanFactory) {
|
||||
return getBeanOfType(beanFactory, TASK_EXECUTOR_BEAN_NAME, TaskExecutor.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link ConversionService} bean whose name is
|
||||
* "yarnConversionService" if available.
|
||||
*
|
||||
* @param beanFactory
|
||||
* BeanFactory for lookup, must not be null.
|
||||
*
|
||||
* @return The {@link ConversionService} bean whose name is
|
||||
* "yarnConversionService" if available.
|
||||
*/
|
||||
public static ConversionService getConversionService(BeanFactory beanFactory) {
|
||||
return getBeanOfType(beanFactory, CONVERSION_SERVICE_BEAN_NAME, ConversionService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link StandardEvaluationContext} bean whose name is
|
||||
* "yarnEvaluationContext" if available.
|
||||
*
|
||||
* @param beanFactory BeanFactory for lookup, must not be null.
|
||||
*
|
||||
* @return the instance of {@link StandardEvaluationContext} bean whose name
|
||||
* is "yarnEvaluationContext" .
|
||||
*/
|
||||
public static StandardEvaluationContext getEvaluationContext(BeanFactory beanFactory) {
|
||||
return getBeanOfType(beanFactory, EVALUATION_CONTEXT_BEAN_NAME, StandardEvaluationContext.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a bean from a factory with a given name and type.
|
||||
*
|
||||
* @param beanFactory the bean factory
|
||||
* @param beanName the bean name
|
||||
* @param type the type as of a class
|
||||
* @return Bean known to a bean factory, null if not found.
|
||||
*/
|
||||
private static <T> T getBeanOfType(BeanFactory beanFactory, String beanName, Class<T> type) {
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
if (!beanFactory.containsBean(beanName)) {
|
||||
return null;
|
||||
}
|
||||
return beanFactory.getBean(beanName, type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
|
||||
/**
|
||||
* A {@link MethodFilter} implementation that will match unique methods.
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class UniqueMethodFilter implements MethodFilter {
|
||||
|
||||
private final List<Method> uniqueMethods = new ArrayList<Method>();
|
||||
|
||||
public UniqueMethodFilter(Class<?> targetClass) {
|
||||
ArrayList<Method> allMethods = new ArrayList<Method>(Arrays.asList(targetClass.getMethods()));
|
||||
for (Method method : allMethods) {
|
||||
this.uniqueMethods.add(org.springframework.util.ClassUtils.getMostSpecificMethod(method, targetClass));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matches(Method method) {
|
||||
return this.uniqueMethods.contains(method);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.transition;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.state.State;
|
||||
|
||||
public abstract class AbstractExternalTransition<S, E> extends AbstractTransition<S, E> implements Transition<S, E> {
|
||||
|
||||
public AbstractExternalTransition(State<S,E> source, State<S,E> target, Collection<Action> actions, E event, Guard guard) {
|
||||
super(source, target, actions, event, TransitionKind.EXTERNAL, guard);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.transition;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.state.State;
|
||||
|
||||
public class AbstractInternalTransition <S, E> extends AbstractTransition<S, E> implements Transition<S, E> {
|
||||
|
||||
public AbstractInternalTransition(State<S, E> source,Collection<Action> actions, E event, Guard guard) {
|
||||
super(source, source, actions, event, TransitionKind.INTERNAL, guard);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.transition;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.state.State;
|
||||
|
||||
public class AbstractLocalTransition <S, E> extends AbstractTransition<S, E> implements Transition<S, E> {
|
||||
|
||||
public AbstractLocalTransition(State<S, E> source, State<S,E> target,Collection<Action> actions, E event, Guard guard) {
|
||||
super(source, target, actions, event, TransitionKind.LOCAL, guard);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.transition;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.trigger.EventTrigger;
|
||||
import org.springframework.statemachine.trigger.Trigger;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base implementation of a {@link Transition}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public abstract class AbstractTransition<S, E> implements Transition<S, E> {
|
||||
|
||||
private final State<S,E> source;
|
||||
|
||||
private final State<S,E> target;
|
||||
|
||||
private final Collection<Action> actions;
|
||||
|
||||
private final TransitionKind kind;
|
||||
|
||||
private final Guard guard;
|
||||
|
||||
private Trigger<S, E> trigger;
|
||||
|
||||
public AbstractTransition(State<S, E> source, State<S, E> target, Collection<Action> actions, E event,
|
||||
TransitionKind kind, Guard guard) {
|
||||
Assert.notNull(source, "Source must be set");
|
||||
// Assert.notNull(target, "Target must be set");
|
||||
Assert.notNull(kind, "Transition type must be set");
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
this.actions = actions;
|
||||
if (event != null) {
|
||||
this.trigger = new EventTrigger<S, E>(event);
|
||||
}
|
||||
this.kind = kind;
|
||||
this.guard = guard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State<S,E> getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State<S,E> getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Action> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trigger<S, E> getTrigger() {
|
||||
return trigger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transit(StateContext context) {
|
||||
if (guard != null) {
|
||||
if (!guard.evaluate(context)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (actions != null) {
|
||||
for (Action action : actions) {
|
||||
action.execute(context);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransitionKind getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.transition;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.state.State;
|
||||
|
||||
public class DefaultExternalTransition<S, E> extends AbstractExternalTransition<S, E> {
|
||||
|
||||
public DefaultExternalTransition(State<S,E> source, State<S,E> target, Collection<Action> actions, E event, Guard guard) {
|
||||
super(source, target, actions, event, guard);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.transition;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.state.State;
|
||||
|
||||
public class DefaultInternalTransition<S, E> extends AbstractInternalTransition<S, E> {
|
||||
|
||||
public DefaultInternalTransition(State<S, E> source, Collection<Action> actions, E event, Guard guard) {
|
||||
super(source, actions, event, guard);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.transition;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.trigger.Trigger;
|
||||
|
||||
/**
|
||||
* {@code Transition} is something what a state machine associates with a state
|
||||
* changes.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public interface Transition<S, E> {
|
||||
|
||||
boolean transit(StateContext context);
|
||||
|
||||
State<S,E> getSource();
|
||||
|
||||
State<S,E> getTarget();
|
||||
|
||||
Collection<Action> getActions();
|
||||
|
||||
Trigger<S, E> getTrigger();
|
||||
|
||||
TransitionKind getKind();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.transition;
|
||||
|
||||
/**
|
||||
* Defines enumeration of a {@link Transition} kind. This is uses within a
|
||||
* transition to indicate whether its type is external, internal or local.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public enum TransitionKind {
|
||||
|
||||
/** Indicates an external transition kind. */
|
||||
EXTERNAL,
|
||||
|
||||
/** Indicates an internal transition kind. */
|
||||
INTERNAL,
|
||||
|
||||
/** Indicates a local transition kind. */
|
||||
LOCAL
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.trigger;
|
||||
|
||||
public class EventTrigger<S, E> implements Trigger<S, E> {
|
||||
|
||||
private final E event;
|
||||
|
||||
public EventTrigger(E event) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(E event) {
|
||||
return this.event.equals(event);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.trigger;
|
||||
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* {@code Trigger} is the cause of the {@link Transition}. Cause is usually an
|
||||
* event but can be some other signal or a change in some condition.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public interface Trigger<S,E> {
|
||||
|
||||
boolean evaluate(E event);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
|
||||
/**
|
||||
* Base class for stace machine tests.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractStateMachineTests {
|
||||
|
||||
public enum TestStates {
|
||||
SI,S1,S2,S3,S4
|
||||
}
|
||||
|
||||
public enum TestEvents {
|
||||
E1,E2,E3,E4
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class BaseConfig {
|
||||
|
||||
@Bean
|
||||
public TaskExecutor taskExecutor() {
|
||||
return new SyncTaskExecutor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TestEntryAction extends AbstractTestAction {
|
||||
}
|
||||
|
||||
public static class TestExitAction extends AbstractTestAction {
|
||||
}
|
||||
|
||||
public static class TestAction extends AbstractTestAction {
|
||||
}
|
||||
|
||||
public static class TestGuard implements Guard {
|
||||
|
||||
public CountDownLatch onEvaluateLatch = new CountDownLatch(1);
|
||||
|
||||
boolean evaluationResult = true;
|
||||
|
||||
public TestGuard() {
|
||||
}
|
||||
|
||||
public TestGuard(boolean evaluationResult) {
|
||||
this.evaluationResult = evaluationResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(StateContext context) {
|
||||
onEvaluateLatch.countDown();
|
||||
return evaluationResult;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class AbstractTestAction implements Action {
|
||||
|
||||
public CountDownLatch onExecuteLatch = new CountDownLatch(1);
|
||||
|
||||
@Override
|
||||
public void execute(StateContext context) {
|
||||
onExecuteLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.statemachine.EnumStateMachine;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.state.EnumState;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.DefaultExternalTransition;
|
||||
import org.springframework.statemachine.transition.DefaultInternalTransition;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
public class EnumStateMachineTests extends AbstractStateMachineTests {
|
||||
|
||||
@Test
|
||||
public void testSimpleStateSwitch() {
|
||||
|
||||
State<TestStates,TestEvents> stateSI = new EnumState<TestStates,TestEvents>(TestStates.SI);
|
||||
State<TestStates,TestEvents> stateS1 = new EnumState<TestStates,TestEvents>(TestStates.S1);
|
||||
State<TestStates,TestEvents> stateS2 = new EnumState<TestStates,TestEvents>(TestStates.S2);
|
||||
State<TestStates,TestEvents> stateS3 = new EnumState<TestStates,TestEvents>(TestStates.S3);
|
||||
|
||||
Collection<State<TestStates,TestEvents>> states = new ArrayList<State<TestStates,TestEvents>>();
|
||||
states.add(stateSI);
|
||||
states.add(stateS1);
|
||||
states.add(stateS2);
|
||||
states.add(stateS3);
|
||||
|
||||
Collection<Transition<TestStates,TestEvents>> transitions = new ArrayList<Transition<TestStates,TestEvents>>();
|
||||
|
||||
Collection<Action> actionsFromSIToS1 = new ArrayList<Action>();
|
||||
actionsFromSIToS1.add(new LoggingAction("actionsFromSIToS1"));
|
||||
DefaultExternalTransition<TestStates,TestEvents> transitionFromSIToS1 =
|
||||
new DefaultExternalTransition<TestStates,TestEvents>(stateSI, stateS1, actionsFromSIToS1, TestEvents.E1, null);
|
||||
|
||||
Collection<Action> actionsFromS1ToS2 = new ArrayList<Action>();
|
||||
actionsFromS1ToS2.add(new LoggingAction("actionsFromS1ToS2"));
|
||||
DefaultExternalTransition<TestStates,TestEvents> transitionFromS1ToS2 =
|
||||
new DefaultExternalTransition<TestStates,TestEvents>(stateS1, stateS2, actionsFromS1ToS2, TestEvents.E2, null);
|
||||
|
||||
Collection<Action> actionsFromS2ToS3 = new ArrayList<Action>();
|
||||
actionsFromS1ToS2.add(new LoggingAction("actionsFromS2ToS3"));
|
||||
DefaultExternalTransition<TestStates,TestEvents> transitionFromS2ToS3 =
|
||||
new DefaultExternalTransition<TestStates,TestEvents>(stateS2, stateS3, actionsFromS2ToS3, TestEvents.E3, null);
|
||||
|
||||
transitions.add(transitionFromSIToS1);
|
||||
transitions.add(transitionFromS1ToS2);
|
||||
transitions.add(transitionFromS2ToS3);
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.start();
|
||||
|
||||
State<TestStates,TestEvents> initialState = machine.getInitialState();
|
||||
assertThat(initialState, is(stateSI));
|
||||
|
||||
State<TestStates,TestEvents> state = machine.getState();
|
||||
assertThat(state, is(stateSI));
|
||||
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
|
||||
state = machine.getState();
|
||||
assertThat(state, is(stateS1));
|
||||
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E2).build());
|
||||
state = machine.getState();
|
||||
assertThat(state, is(stateS2));
|
||||
|
||||
// not processed
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
|
||||
state = machine.getState();
|
||||
assertThat(state, is(stateS2));
|
||||
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E3).build());
|
||||
state = machine.getState();
|
||||
assertThat(state, is(stateS3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeferredEvents() {
|
||||
|
||||
Collection<TestEvents> deferred = new ArrayList<TestEvents>();
|
||||
deferred.add(TestEvents.E2);
|
||||
deferred.add(TestEvents.E3);
|
||||
|
||||
// states
|
||||
State<TestStates,TestEvents> stateSI = new EnumState<TestStates,TestEvents>(TestStates.SI, deferred);
|
||||
State<TestStates,TestEvents> stateS1 = new EnumState<TestStates,TestEvents>(TestStates.S1);
|
||||
State<TestStates,TestEvents> stateS2 = new EnumState<TestStates,TestEvents>(TestStates.S2);
|
||||
State<TestStates,TestEvents> stateS3 = new EnumState<TestStates,TestEvents>(TestStates.S3);
|
||||
|
||||
Collection<State<TestStates,TestEvents>> states = new ArrayList<State<TestStates,TestEvents>>();
|
||||
states.add(stateSI);
|
||||
states.add(stateS1);
|
||||
states.add(stateS2);
|
||||
states.add(stateS3);
|
||||
|
||||
// transitions
|
||||
Collection<Transition<TestStates,TestEvents>> transitions = new ArrayList<Transition<TestStates,TestEvents>>();
|
||||
|
||||
Collection<Action> actionsFromSIToS1 = new ArrayList<Action>();
|
||||
actionsFromSIToS1.add(new LoggingAction("actionsFromSIToS1"));
|
||||
DefaultExternalTransition<TestStates,TestEvents> transitionFromSIToS1 =
|
||||
new DefaultExternalTransition<TestStates,TestEvents>(stateSI, stateS1, actionsFromSIToS1, TestEvents.E1, null);
|
||||
|
||||
Collection<Action> actionsFromS1ToS2 = new ArrayList<Action>();
|
||||
actionsFromS1ToS2.add(new LoggingAction("actionsFromS1ToS2"));
|
||||
DefaultExternalTransition<TestStates,TestEvents> transitionFromS1ToS2 =
|
||||
new DefaultExternalTransition<TestStates,TestEvents>(stateS1, stateS2, actionsFromS1ToS2, TestEvents.E2, null);
|
||||
|
||||
Collection<Action> actionsFromS2ToS3 = new ArrayList<Action>();
|
||||
actionsFromS1ToS2.add(new LoggingAction("actionsFromS2ToS3"));
|
||||
DefaultExternalTransition<TestStates,TestEvents> transitionFromS2ToS3 =
|
||||
new DefaultExternalTransition<TestStates,TestEvents>(stateS2, stateS3, actionsFromS2ToS3, TestEvents.E3, null);
|
||||
|
||||
transitions.add(transitionFromSIToS1);
|
||||
transitions.add(transitionFromS1ToS2);
|
||||
transitions.add(transitionFromS2ToS3);
|
||||
|
||||
// create machine
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
// StateMachine<State<TestStates, TestEvents>, TestEvents> machine2 = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.start();
|
||||
|
||||
State<TestStates,TestEvents> initialState = machine.getInitialState();
|
||||
assertThat(initialState, is(stateSI));
|
||||
|
||||
State<TestStates,TestEvents> state = machine.getState();
|
||||
assertThat(state, is(stateSI));
|
||||
|
||||
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E2).build());
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E3).build());
|
||||
state = machine.getState();
|
||||
assertThat(state, is(stateSI));
|
||||
|
||||
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
|
||||
state = machine.getState();
|
||||
assertThat(state, is(stateS3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInternalTransitions() {
|
||||
State<TestStates,TestEvents> stateSI = new EnumState<TestStates,TestEvents>(TestStates.SI);
|
||||
|
||||
Collection<State<TestStates,TestEvents>> states = new ArrayList<State<TestStates,TestEvents>>();
|
||||
states.add(stateSI);
|
||||
|
||||
Collection<Action> actionsInSI = new ArrayList<Action>();
|
||||
actionsInSI.add(new LoggingAction("actionsInSI"));
|
||||
DefaultInternalTransition<TestStates,TestEvents> transitionInternalSI =
|
||||
new DefaultInternalTransition<TestStates,TestEvents>(stateSI, actionsInSI, TestEvents.E1, null);
|
||||
|
||||
// transitions
|
||||
Collection<Transition<TestStates,TestEvents>> transitions = new ArrayList<Transition<TestStates,TestEvents>>();
|
||||
transitions.add(transitionInternalSI);
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.start();
|
||||
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
|
||||
}
|
||||
|
||||
private static class LoggingAction implements Action {
|
||||
|
||||
private static final Log log = LogFactory.getLog(LoggingAction.class);
|
||||
|
||||
private String message;
|
||||
|
||||
public LoggingAction(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(StateContext context) {
|
||||
log.info("Hello from LoggingAction " + message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.statemachine.config.EnableStateMachineFactory;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.EnumStateMachineFactory;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.state.State;
|
||||
|
||||
public class StateMachineFactoryTests extends AbstractStateMachineTests {
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Test
|
||||
public void testMachineFromFactory() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
|
||||
|
||||
EnumStateMachineFactory<TestStates, TestEvents> stateMachineFactory =
|
||||
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINEFACTORY, EnumStateMachineFactory.class);
|
||||
StateMachine<State<TestStates, TestEvents>, TestEvents> machine = stateMachineFactory.getStateMachine();
|
||||
|
||||
assertThat(machine.getState().getId(), is(TestStates.S1));
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
|
||||
assertThat(machine.getState().getId(), is(TestStates.S2));
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachineFactory
|
||||
static class Config extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(TestStates.S1)
|
||||
.state(TestStates.S1)
|
||||
.state(TestStates.S2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.S1)
|
||||
.target(TestStates.S2)
|
||||
.event(TestEvents.E1);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskExecutor taskExecutor() {
|
||||
return new SyncTaskExecutor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.statemachine.EnumStateMachine;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachineSystemConstants;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
|
||||
public class StateMachineTests extends AbstractStateMachineTests {
|
||||
|
||||
@Test
|
||||
public void test1() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
|
||||
assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
@SuppressWarnings("unchecked")
|
||||
EnumStateMachine<TestStates,TestEvents> machine =
|
||||
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
|
||||
assertThat(machine, notNullValue());
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).setHeader("foo", "jee1").build());
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E2).setHeader("foo", "jee2").build());
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E4).setHeader("foo", "jee2").build());
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
private static class LoggingAction implements Action {
|
||||
|
||||
private static final Log log = LogFactory.getLog(StateMachineTests.LoggingAction.class);
|
||||
|
||||
private String message;
|
||||
|
||||
public LoggingAction(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(StateContext context) {
|
||||
log.info("Hello from LoggingAction " + message + " foo=" + context.getMessageHeaders().get("foo"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class Config extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(TestStates.S1)
|
||||
.state(TestStates.S1)
|
||||
.state(TestStates.S2)
|
||||
.state(TestStates.S3, TestEvents.E4)
|
||||
.state(TestStates.S4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.S1)
|
||||
.target(TestStates.S2)
|
||||
.event(TestEvents.E1)
|
||||
.action(loggingAction())
|
||||
.action(loggingAction())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S2)
|
||||
.target(TestStates.S3)
|
||||
.event(TestEvents.E2)
|
||||
.action(loggingAction())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S3)
|
||||
.target(TestStates.S4)
|
||||
.event(TestEvents.E3)
|
||||
.action(loggingAction())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S4)
|
||||
.target(TestStates.S3)
|
||||
.event(TestEvents.E4)
|
||||
.action(loggingAction());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LoggingAction loggingAction() {
|
||||
return new LoggingAction("as bean");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskExecutor taskExecutor() {
|
||||
return new SyncTaskExecutor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Utils for tests.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class TestUtils {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T readField(String name, Object target) throws Exception {
|
||||
Field field = null;
|
||||
Class<?> clazz = target.getClass();
|
||||
do {
|
||||
try {
|
||||
field = clazz.getDeclaredField(name);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
|
||||
clazz = clazz.getSuperclass();
|
||||
} while (field == null && !clazz.equals(Object.class));
|
||||
|
||||
if (field == null)
|
||||
throw new IllegalArgumentException("Cannot find field '" + name + "' in the class hierarchy of "
|
||||
+ target.getClass());
|
||||
field.setAccessible(true);
|
||||
return (T) field.get(target);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T callMethod(String name, Object target) throws Exception {
|
||||
Class<?> clazz = target.getClass();
|
||||
Method method = ReflectionUtils.findMethod(clazz, name);
|
||||
|
||||
if (method == null)
|
||||
throw new IllegalArgumentException("Cannot find method '" + method + "' in the class hierarchy of "
|
||||
+ target.getClass());
|
||||
method.setAccessible(true);
|
||||
return (T) ReflectionUtils.invokeMethod(method, target);
|
||||
}
|
||||
|
||||
public static void setField(String name, Object target, Object value) throws Exception {
|
||||
Field field = null;
|
||||
Class<?> clazz = target.getClass();
|
||||
do {
|
||||
try {
|
||||
field = clazz.getDeclaredField(name);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
|
||||
clazz = clazz.getSuperclass();
|
||||
} while (field == null && !clazz.equals(Object.class));
|
||||
|
||||
if (field == null)
|
||||
throw new IllegalArgumentException("Cannot find field '" + name + "' in the class hierarchy of "
|
||||
+ target.getClass());
|
||||
field.setAccessible(true);
|
||||
field.set(target, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T callMethod(String name, Object target, Object[] args, Class<?>[] argsTypes) throws Exception {
|
||||
Class<?> clazz = target.getClass();
|
||||
Method method = ReflectionUtils.findMethod(clazz, name, argsTypes);
|
||||
|
||||
if (method == null)
|
||||
throw new IllegalArgumentException("Cannot find method '" + method + "' in the class hierarchy of "
|
||||
+ target.getClass());
|
||||
method.setAccessible(true);
|
||||
return (T) ReflectionUtils.invokeMethod(method, target, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.action;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.statemachine.AbstractStateMachineTests;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.StateMachineSystemConstants;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
|
||||
/**
|
||||
* Tests for state machine actions.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class ActionTests extends AbstractStateMachineTests {
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Test
|
||||
public void testTransitionActions() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config1.class);
|
||||
assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
StateMachine<TestStates,TestEvents> machine =
|
||||
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, StateMachine.class);
|
||||
|
||||
TestCountAction testAction1 = ctx.getBean("testAction1", TestCountAction.class);
|
||||
TestCountAction testAction2 = ctx.getBean("testAction2", TestCountAction.class);
|
||||
TestCountAction testAction3 = ctx.getBean("testAction3", TestCountAction.class);
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E2).build());
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E3).build());
|
||||
assertThat(testAction1.count, is(1));
|
||||
assertThat(testAction2.count, is(1));
|
||||
assertThat(testAction3.count, is(1));
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventFromAction() {
|
||||
|
||||
}
|
||||
|
||||
private static class TestCountAction implements Action {
|
||||
|
||||
int count = 0;
|
||||
|
||||
public TestCountAction() {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(StateContext context) {
|
||||
count++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(TestStates.S1)
|
||||
.state(TestStates.S1)
|
||||
.state(TestStates.S2)
|
||||
.state(TestStates.S3)
|
||||
.state(TestStates.S4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.S1)
|
||||
.target(TestStates.S2)
|
||||
.event(TestEvents.E1)
|
||||
.action(testAction1())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S2)
|
||||
.target(TestStates.S3)
|
||||
.event(TestEvents.E2)
|
||||
.action(testAction2())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S3)
|
||||
.target(TestStates.S4)
|
||||
.event(TestEvents.E3)
|
||||
.action(testAction3());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestCountAction testAction1() {
|
||||
return new TestCountAction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestCountAction testAction2() {
|
||||
return new TestCountAction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestCountAction testAction3() {
|
||||
return new TestCountAction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskExecutor taskExecutor() {
|
||||
return new SyncTaskExecutor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.statemachine.AbstractStateMachineTests;
|
||||
import org.springframework.statemachine.EnumStateMachine;
|
||||
import org.springframework.statemachine.StateMachineSystemConstants;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.processor.StateMachineAnnotationPostProcessor;
|
||||
|
||||
public class MethodAnnotationTests extends AbstractStateMachineTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testMethodAnnotations() throws Exception {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BaseConfig.class, AnnoConfig.class, BeanConfig1.class, Config1.class);
|
||||
|
||||
EnumStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
|
||||
assertThat(context.containsBean("fooMachine"), is(true));
|
||||
|
||||
Bean1 bean1 = context.getBean(Bean1.class);
|
||||
|
||||
// this event should cause 'method1' to get called
|
||||
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
|
||||
|
||||
assertThat(bean1.onMethod1Latch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat(bean1.onOnTransitionFromS2ToS3Latch.await(2, TimeUnit.SECONDS), is(false));
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
@WithStateMachine(name = StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)
|
||||
static class Bean1 {
|
||||
|
||||
CountDownLatch onMethod1Latch = new CountDownLatch(1);
|
||||
CountDownLatch onOnTransitionFromS2ToS3Latch = new CountDownLatch(1);
|
||||
|
||||
@OnTransition(source = "S1", target = "S2")
|
||||
public void method1() {
|
||||
onMethod1Latch.countDown();
|
||||
}
|
||||
|
||||
@OnTransition
|
||||
public void onTransitionFromS2ToS3() {
|
||||
onOnTransitionFromS2ToS3Latch.countDown();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class BeanConfig1 {
|
||||
|
||||
@Bean
|
||||
public Bean1 bean1() {
|
||||
return new Bean1();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class AnnoConfig {
|
||||
|
||||
@Bean(name="org.springframework.statemachine.internal.springStateMachineAnnotationPostProcessor")
|
||||
public StateMachineAnnotationPostProcessor springStateMachineAnnotationPostProcessor() {
|
||||
return new StateMachineAnnotationPostProcessor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine(name = {StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, "fooMachine"})
|
||||
static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(TestStates.S1)
|
||||
.states(EnumSet.allOf(TestStates.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.S1)
|
||||
.target(TestStates.S2)
|
||||
.event(TestEvents.E1)
|
||||
.guard(testGuard())
|
||||
.action(testAction())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S2)
|
||||
.target(TestStates.S3)
|
||||
.event(TestEvents.E2);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestGuard testGuard() {
|
||||
return new TestGuard();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestAction testAction() {
|
||||
return new TestAction();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.statemachine.AbstractStateMachineTests;
|
||||
import org.springframework.statemachine.EnumStateMachine;
|
||||
import org.springframework.statemachine.StateMachineSystemConstants;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
|
||||
/**
|
||||
* Tests for state machine configuration.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class ConfigurationTests extends AbstractStateMachineTests {
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Test
|
||||
public void testStates() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config1.class);
|
||||
assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
EnumStateMachine<TestStates,TestEvents> machine =
|
||||
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
|
||||
assertThat(machine, notNullValue());
|
||||
TestAction testAction = ctx.getBean("testAction", TestAction.class);
|
||||
TestGuard testGuard = ctx.getBean("testGuard", TestGuard.class);
|
||||
assertThat(testAction, notNullValue());
|
||||
assertThat(testGuard, notNullValue());
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
public static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(TestStates.S1)
|
||||
.state(TestStates.S1)
|
||||
.state(TestStates.S2)
|
||||
.state(TestStates.S3)
|
||||
.state(TestStates.S4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.S1)
|
||||
.target(TestStates.S2)
|
||||
.event(TestEvents.E1)
|
||||
.guard(testGuard())
|
||||
.action(testAction());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestAction testAction() {
|
||||
return new TestAction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestGuard testGuard() {
|
||||
return new TestGuard();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskExecutor taskExecutor() {
|
||||
return new SyncTaskExecutor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
public static class Config2 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(TestStates.S1)
|
||||
.states(EnumSet.allOf(TestStates.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.config.common.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.statemachine.config.common.annotation.complex.ComplexTestConfig;
|
||||
import org.springframework.statemachine.config.common.annotation.complex.ComplexTestConfigBeanABuilder;
|
||||
import org.springframework.statemachine.config.common.annotation.complex.ComplexTestConfigBeanB;
|
||||
import org.springframework.statemachine.config.common.annotation.complex.ComplexTestConfigBeanBConfigurer;
|
||||
import org.springframework.statemachine.config.common.annotation.complex.ComplexTestConfigBuilder;
|
||||
import org.springframework.statemachine.config.common.annotation.complex.ComplexTestConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.common.annotation.complex.EnableComplexTest;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
|
||||
/**
|
||||
* Tests for generic javaconfig builder/configurer concepts.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(loader=AnnotationConfigContextLoader.class)
|
||||
public class ComplexAnnotationConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext ctx;
|
||||
|
||||
@Test
|
||||
public void testSimpleConfig() throws Exception {
|
||||
assertNotNull(ctx);
|
||||
assertTrue(ctx.containsBean("complexConfig"));
|
||||
ComplexTestConfig config = ctx.getBean("complexConfig", ComplexTestConfig.class);
|
||||
assertThat(config.complexData, notNullValue());
|
||||
assertThat(config.complexData, is("complexData"));
|
||||
|
||||
assertThat(config.complexProperties, notNullValue());
|
||||
assertThat(config.complexProperties.getProperty("complexKey1"), notNullValue());
|
||||
assertThat(config.complexProperties.getProperty("complexKey1"), is("complexValue1"));
|
||||
|
||||
assertThat(config.complexBeanA, notNullValue());
|
||||
assertThat(config.complexBeanA.dataA, notNullValue());
|
||||
assertThat(config.complexBeanA.resources, notNullValue());
|
||||
|
||||
assertThat(config.complexBeanA.dataA, is("complexDataA"));
|
||||
assertThat(config.complexBeanA.resources.size(), is(2));
|
||||
Iterator<Resource> iterator = config.complexBeanA.resources.iterator();
|
||||
String fileName1 = iterator.next().getFilename();
|
||||
String fileName2 = iterator.next().getFilename();
|
||||
String[] fileNames = new String[2];
|
||||
fileNames[0] = fileName1.equals("complexResourceA1") ? fileName1 : fileName2;
|
||||
fileNames[1] = fileName2.equals("complexResourceA2") ? fileName2 : fileName1;
|
||||
assertThat(fileNames[0], is("complexResourceA1"));
|
||||
assertThat(fileNames[1], is("complexResourceA2"));
|
||||
|
||||
assertTrue(ctx.containsBean("complexConfigData"));
|
||||
assertTrue(ctx.containsBean("complexConfigBeanB"));
|
||||
ComplexTestConfigBeanB beanB = ctx.getBean("complexConfigBeanB", ComplexTestConfigBeanB.class);
|
||||
assertThat(beanB.dataB, is("complexDataB"));
|
||||
assertThat(beanB.dataBB, is("complexDataBB"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableComplexTest
|
||||
static class Config extends ComplexTestConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
public void configure(ComplexTestConfigBuilder config) throws Exception {
|
||||
config
|
||||
.withProperties()
|
||||
.property("complexKey1", "complexValue1");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(ComplexTestConfigBeanABuilder beanA) throws Exception {
|
||||
beanA
|
||||
.withResources()
|
||||
.resource("complexResourceA1")
|
||||
.resource("complexResourceA2")
|
||||
.and()
|
||||
.setData("complexDataA");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(ComplexTestConfigBeanBConfigurer beanB) throws Exception {
|
||||
beanB
|
||||
.setData("complexDataB")
|
||||
.setDataBB("complexDataBB")
|
||||
.withResources().and();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.config.common.annotation;
|
||||
|
||||
import org.springframework.statemachine.config.common.annotation.simple.SimpleTestConfigBeanB;
|
||||
|
||||
public class DependencyBean {
|
||||
|
||||
private SimpleTestConfigBeanB beanB;
|
||||
|
||||
public SimpleTestConfigBeanB getBeanB() {
|
||||
return beanB;
|
||||
}
|
||||
|
||||
public void setBeanB(SimpleTestConfigBeanB beanB) {
|
||||
this.beanB = beanB;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.config.common.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.config.common.annotation.complex.ComplexTestConfig;
|
||||
import org.springframework.statemachine.config.common.annotation.complex.ComplexTestConfigBuilder;
|
||||
import org.springframework.statemachine.config.common.annotation.complex.ComplexTestConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.common.annotation.complex.EnableComplexTest;
|
||||
import org.springframework.statemachine.config.common.annotation.simple.EnableSimpleTest;
|
||||
import org.springframework.statemachine.config.common.annotation.simple.SimpleTestConfig;
|
||||
import org.springframework.statemachine.config.common.annotation.simple.SimpleTestConfigBuilder;
|
||||
import org.springframework.statemachine.config.common.annotation.simple.SimpleTestConfigurerAdapter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(loader=AnnotationConfigContextLoader.class)
|
||||
public class MixedAnnotationConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext ctx;
|
||||
|
||||
@Test
|
||||
public void testConfig() throws Exception {
|
||||
assertNotNull(ctx);
|
||||
assertTrue(ctx.containsBean("simpleConfig"));
|
||||
SimpleTestConfig simpleConfig = ctx.getBean("simpleConfig", SimpleTestConfig.class);
|
||||
assertThat(simpleConfig.simpleData, notNullValue());
|
||||
assertThat(simpleConfig.simpleData, is("simpleData"));
|
||||
|
||||
assertThat(simpleConfig.simpleProperties, notNullValue());
|
||||
assertThat(simpleConfig.simpleProperties.getProperty("simpleKey1"), notNullValue());
|
||||
assertThat(simpleConfig.simpleProperties.getProperty("simpleKey1"), is("simpleValue1"));
|
||||
|
||||
assertTrue(ctx.containsBean("complexConfig"));
|
||||
ComplexTestConfig complexConfig = ctx.getBean("complexConfig", ComplexTestConfig.class);
|
||||
assertThat(complexConfig.complexData, notNullValue());
|
||||
assertThat(complexConfig.complexData, is("complexData"));
|
||||
assertThat(complexConfig.complexProperties, notNullValue());
|
||||
assertThat(complexConfig.complexProperties.getProperty("complexKey1"), notNullValue());
|
||||
assertThat(complexConfig.complexProperties.getProperty("complexKey1"), is("complexValue1"));
|
||||
|
||||
assertThat(complexConfig.simpleTestConfig, notNullValue());
|
||||
assertThat(complexConfig.simpleTestConfig.simpleData, notNullValue());
|
||||
assertThat(complexConfig.simpleTestConfig.simpleData, is("simpleData"));
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableSimpleTest
|
||||
static class SimpleConfig extends SimpleTestConfigurerAdapter {
|
||||
@Override
|
||||
public void configure(SimpleTestConfigBuilder config) throws Exception {
|
||||
config
|
||||
.withProperties()
|
||||
.property("simpleKey1", "simpleValue1");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableComplexTest
|
||||
static class ComplexConfig extends ComplexTestConfigurerAdapter {
|
||||
@Override
|
||||
public void configure(ComplexTestConfigBuilder config) throws Exception {
|
||||
config
|
||||
.withProperties()
|
||||
.property("complexKey1", "complexValue1");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user