Remove dependency to Spring Shell
This commit updates the samples to use Java's standard CLI utilities instead of Spring Shell. Resolves #1184
This commit is contained in:
@@ -15,18 +15,21 @@
|
||||
*/
|
||||
package demo;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.shell.command.annotation.Command;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Command
|
||||
import java.io.InputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
|
||||
@Configuration
|
||||
public class AbstractStateMachineCommands<S, E> {
|
||||
|
||||
@Autowired
|
||||
@@ -36,54 +39,74 @@ public class AbstractStateMachineCommands<S, E> {
|
||||
return stateMachine;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier("stateChartModel")
|
||||
private String stateChartModel;
|
||||
|
||||
@Command(command = "sm state", description = "Prints current state")
|
||||
public String state() {
|
||||
State<S, E> state = stateMachine.getState();
|
||||
if (state != null) {
|
||||
return StringUtils.collectionToCommaDelimitedString(state.getIds());
|
||||
} else {
|
||||
return "No state";
|
||||
}
|
||||
}
|
||||
|
||||
@Command(command = "sm start", description = "Start a state machine")
|
||||
public String start() {
|
||||
stateMachine.startReactively().subscribe();
|
||||
return "State machine started";
|
||||
}
|
||||
|
||||
@Command(command = "sm stop", description = "Stop a state machine")
|
||||
public String stop() {
|
||||
stateMachine.stopReactively().subscribe();
|
||||
return "State machine stopped";
|
||||
}
|
||||
|
||||
@Command(command = "sm print", description = "Print state machine")
|
||||
public String print() {
|
||||
return stateChartModel;
|
||||
}
|
||||
|
||||
@Command(command = "sm variables", description = "Prints extended state variables")
|
||||
public String variables() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
Set<Entry<Object, Object>> entrySet = stateMachine.getExtendedState().getVariables().entrySet();
|
||||
Iterator<Entry<Object, Object>> iterator = entrySet.iterator();
|
||||
if (entrySet.size() > 0) {
|
||||
while (iterator.hasNext()) {
|
||||
Entry<Object, Object> e = iterator.next();
|
||||
buf.append(e.getKey() + "=" + e.getValue());
|
||||
if (iterator.hasNext()) {
|
||||
buf.append("\n");
|
||||
@Bean
|
||||
public Command state() {
|
||||
return new BasicCommand("state", "Prints current state") {
|
||||
public String execute(String[] args) {
|
||||
State<S, E> state = stateMachine.getState();
|
||||
if (state != null) {
|
||||
return StringUtils.collectionToCommaDelimitedString(state.getIds());
|
||||
} else {
|
||||
return "No state";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buf.append("No variables");
|
||||
}
|
||||
return buf.toString();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@Bean
|
||||
public Command start() {
|
||||
return new BasicCommand("start", "Start a state machine") {
|
||||
public String execute(String[] args) {
|
||||
stateMachine.startReactively().subscribe();
|
||||
return "State machine started";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Command stop() {
|
||||
return new BasicCommand("stop", "Stop a state machine") {
|
||||
public String execute(String[] args) {
|
||||
stateMachine.stopReactively().subscribe();
|
||||
return "State machine stopped";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Command print() {
|
||||
return new BasicCommand("print", "Print state machine") {
|
||||
public String execute(String[] args) throws Exception {
|
||||
ClassPathResource model = new ClassPathResource("statechartmodel.txt");
|
||||
InputStream inputStream = model.getInputStream();
|
||||
Scanner scanner = new Scanner(inputStream);
|
||||
String content = scanner.useDelimiter("\\Z").next();
|
||||
scanner.close();
|
||||
return content;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Command variables() {
|
||||
return new BasicCommand("variables", "Prints extended state variables") {
|
||||
public String execute(String[] args) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
Set<Entry<Object, Object>> entrySet = stateMachine.getExtendedState().getVariables().entrySet();
|
||||
Iterator<Entry<Object, Object>> iterator = entrySet.iterator();
|
||||
if (entrySet.size() > 0) {
|
||||
while (iterator.hasNext()) {
|
||||
Entry<Object, Object> e = iterator.next();
|
||||
buf.append(e.getKey() + "=" + e.getValue());
|
||||
if (iterator.hasNext()) {
|
||||
buf.append("\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buf.append("No variables");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2015-2025 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
|
||||
*
|
||||
* https://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 demo;
|
||||
|
||||
/**
|
||||
* Basic implementation of the {@link Command} interface.
|
||||
* This class provides a simple way to create commands with a name and description.
|
||||
* It can be extended to implement specific command logic.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine
|
||||
*/
|
||||
public class BasicCommand implements Command {
|
||||
|
||||
private final String name;
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* Create a new {@link BasicCommand} with a name and description.
|
||||
*
|
||||
* @param name the command name
|
||||
* @param description the command description
|
||||
*/
|
||||
public BasicCommand(String name, String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(String[] args) throws Exception {
|
||||
throw new UnsupportedOperationException("Not implemented yet");
|
||||
}
|
||||
}
|
||||
@@ -15,19 +15,31 @@
|
||||
*/
|
||||
package demo;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.shell.jline.PromptProvider;
|
||||
import org.springframework.stereotype.Component;
|
||||
/**
|
||||
* Simple interface for command line commands.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine
|
||||
*/
|
||||
public interface Command {
|
||||
|
||||
@Component
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class StateMachinePromptProvider implements PromptProvider {
|
||||
/**
|
||||
* The command name.
|
||||
* @return the command name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
@Override
|
||||
public AttributedString getPrompt() {
|
||||
return AttributedString.fromAnsi("sm>");
|
||||
}
|
||||
/**
|
||||
* The command description.
|
||||
* @return the command description
|
||||
*/
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
* Execute the command.
|
||||
* @param args the command arguments
|
||||
* @return the message to be printed to the console
|
||||
* @throws Exception in case of error
|
||||
*/
|
||||
String execute(String[] args) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2015-2025 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
|
||||
*
|
||||
* https://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 demo;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.Console;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Command line runner that executes commands.
|
||||
* <p>
|
||||
* This class implements the {@link ApplicationRunner} interface and is responsible for
|
||||
* executing commands in the command line interface. It retrieves all beans of type {@link Command}
|
||||
* from the application context and allows the user to execute them interactively.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine
|
||||
*/
|
||||
@Component
|
||||
public class CommandRunner implements ApplicationRunner, ApplicationContextAware, Ordered {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
Map<String, Command> commands = this.applicationContext.getBeansOfType(Command.class);
|
||||
Console console = System.console();
|
||||
if (console == null) {
|
||||
System.err.println("No console available.");
|
||||
return;
|
||||
}
|
||||
|
||||
final String lineSeparator = System.lineSeparator();
|
||||
console.printf("Available commands:" + lineSeparator);
|
||||
for (Command command : commands.values()) {
|
||||
console.printf(" " + command.getName() + ": " + command.getDescription() + lineSeparator);
|
||||
}
|
||||
console.printf(" quit: exit" + lineSeparator);
|
||||
|
||||
String commandString = "";
|
||||
while (!commandString.equalsIgnoreCase("quit")) {
|
||||
console.printf("sm>");
|
||||
commandString = console.readLine();
|
||||
String[] tokens = commandString.split(" ");
|
||||
String commandName = tokens[0];
|
||||
String[] commandArgs = Stream.of(tokens).skip(1).limit(tokens.length).toArray(String[]::new);
|
||||
if (commandName.equalsIgnoreCase("quit")) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Command command = commands.get(commandName);
|
||||
if (command == null) {
|
||||
console.printf("Command not found: " + commandString + lineSeparator);
|
||||
continue;
|
||||
}
|
||||
console.printf(command.execute(commandArgs));
|
||||
console.printf(lineSeparator);
|
||||
} catch (Exception exception) {
|
||||
console.printf("Error while executing command: " + commandString + lineSeparator);
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
console.printf("bye!" + lineSeparator);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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 demo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Scanner;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.statemachine.event.OnStateEntryEvent;
|
||||
import org.springframework.statemachine.event.OnStateExitEvent;
|
||||
import org.springframework.statemachine.event.OnTransitionEvent;
|
||||
import org.springframework.statemachine.event.StateMachineEvent;
|
||||
import org.springframework.statemachine.transition.TransitionKind;
|
||||
|
||||
@Configuration
|
||||
public class CommonConfiguration {
|
||||
|
||||
private final static Log log = LogFactory.getLog(CommonConfiguration.class);
|
||||
|
||||
@Configuration
|
||||
static class ApplicationConfig {
|
||||
|
||||
@Bean
|
||||
public TestEventListener testEventListener() {
|
||||
return new TestEventListener();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String stateChartModel() throws IOException {
|
||||
ClassPathResource model = new ClassPathResource("statechartmodel.txt");
|
||||
InputStream inputStream = model.getInputStream();
|
||||
Scanner scanner = new Scanner(inputStream);
|
||||
String content = scanner.useDelimiter("\\Z").next();
|
||||
scanner.close();
|
||||
return content;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestEventListener implements ApplicationListener<StateMachineEvent> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(StateMachineEvent event) {
|
||||
if (event instanceof OnStateEntryEvent) {
|
||||
OnStateEntryEvent e = (OnStateEntryEvent)event;
|
||||
log.info("Entry state " + e.getState().getId());
|
||||
} else if (event instanceof OnStateExitEvent) {
|
||||
OnStateExitEvent e = (OnStateExitEvent)event;
|
||||
log.info("Exit state " + e.getState().getId());
|
||||
} else if (event instanceof OnTransitionEvent) {
|
||||
OnTransitionEvent e = (OnTransitionEvent)event;
|
||||
if (e.getTransition().getKind() == TransitionKind.INTERNAL) {
|
||||
log.info("Internal transition source=" + e.getTransition().getSource().getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package demo;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.statemachine.event.OnStateEntryEvent;
|
||||
import org.springframework.statemachine.event.OnStateExitEvent;
|
||||
import org.springframework.statemachine.event.OnTransitionEvent;
|
||||
import org.springframework.statemachine.event.StateMachineEvent;
|
||||
import org.springframework.statemachine.transition.TransitionKind;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
class StateMachineEventListener implements ApplicationListener<StateMachineEvent> {
|
||||
|
||||
private final static Log log = LogFactory.getLog(StateMachineEventListener.class);
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(StateMachineEvent stateMachineEvent) {
|
||||
if (stateMachineEvent instanceof OnStateEntryEvent event) {
|
||||
log.info("Entry state " + event.getState().getId());
|
||||
} else if (stateMachineEvent instanceof OnStateExitEvent event) {
|
||||
log.info("Exit state " + event.getState().getId());
|
||||
} else if (stateMachineEvent instanceof OnTransitionEvent event) {
|
||||
if (event.getTransition().getKind() == TransitionKind.INTERNAL) {
|
||||
log.info("Internal transition source=" + event.getTransition().getSource().getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
|
||||
<context:component-scan base-package="demo" />
|
||||
</beans>
|
||||
Reference in New Issue
Block a user