Skip to content

Commit 3258772

Browse files
committed
Add initial support for Spring Boot auto-configuration
1 parent 64627a5 commit 3258772

File tree

37 files changed

+239
-2560
lines changed

37 files changed

+239
-2560
lines changed

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,12 @@
8989
<modules>
9090
<module>spring-shell-core</module>
9191
<!-- <module>spring-shell-test</module>-->
92-
<!-- <module>spring-shell-autoconfigure</module>-->
92+
<module>spring-shell-autoconfigure</module>
9393
<!-- <module>spring-shell-test-autoconfigure</module>-->
9494
<!-- <module>spring-shell-docs</module>-->
9595
<!-- <module>spring-shell-dependencies</module>-->
9696
<module>spring-shell-samples</module>
97-
<!-- <module>spring-shell-starters</module>-->
97+
<module>spring-shell-starters</module>
9898
</modules>
9999

100100
<build>

spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CommandRegistrationCustomizer.java

Lines changed: 0 additions & 34 deletions
This file was deleted.

spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CommandRegistryAutoConfiguration.java

Lines changed: 56 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -15,116 +15,78 @@
1515
*/
1616
package org.springframework.shell.boot;
1717

18-
import java.util.List;
19-
import java.util.stream.Collectors;
18+
import java.lang.reflect.Method;
19+
import java.util.Map;
20+
import java.util.Set;
2021

21-
import org.springframework.beans.factory.ObjectProvider;
22+
import org.apache.commons.logging.Log;
23+
import org.apache.commons.logging.LogFactory;
24+
25+
import org.springframework.aop.support.AopUtils;
26+
import org.springframework.beans.factory.config.BeanDefinition;
2227
import org.springframework.boot.autoconfigure.AutoConfiguration;
23-
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
28+
import org.springframework.boot.autoconfigure.SpringBootApplication;
2429
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
25-
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
26-
import org.springframework.boot.context.properties.EnableConfigurationProperties;
30+
import org.springframework.context.ApplicationContext;
2731
import org.springframework.context.annotation.Bean;
28-
import org.springframework.shell.core.MethodTargetRegistrar;
29-
import org.springframework.shell.boot.SpringShellProperties.Help;
32+
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
33+
import org.springframework.core.MethodIntrospector;
34+
import org.springframework.core.annotation.AnnotatedElementUtils;
35+
import org.springframework.shell.core.ShellConfigurationException;
36+
import org.springframework.shell.core.command.Command;
3037
import org.springframework.shell.core.command.CommandRegistry;
31-
import org.springframework.shell.core.command.CommandRegistryCustomizer;
32-
import org.springframework.shell.core.command.CommandRegistration;
33-
import org.springframework.shell.core.command.CommandRegistration.BuilderSupplier;
34-
import org.springframework.shell.core.command.CommandRegistration.OptionNameModifier;
35-
import org.springframework.shell.core.command.support.OptionNameModifierSupport;
36-
import org.springframework.shell.core.command.CommandResolver;
37-
import org.springframework.shell.core.context.ShellContext;
38+
import org.springframework.shell.core.command.annotation.support.CommandFactoryBean;
39+
import org.springframework.util.ClassUtils;
40+
import org.springframework.util.ReflectionUtils;
3841

3942
@AutoConfiguration
40-
@EnableConfigurationProperties(SpringShellProperties.class)
4143
public class CommandRegistryAutoConfiguration {
4244

43-
@Bean
44-
@ConditionalOnMissingBean(CommandRegistry.class)
45-
public CommandRegistry commandRegistry(ObjectProvider<MethodTargetRegistrar> methodTargetRegistrars,
46-
ObjectProvider<CommandResolver> commandResolvers,
47-
ObjectProvider<CommandRegistryCustomizer> commandRegistryCustomizers, ShellContext shellContext) {
48-
List<CommandResolver> resolvers = commandResolvers.orderedStream().collect(Collectors.toList());
49-
CommandRegistry registry = CommandRegistry.of(resolvers, shellContext);
50-
methodTargetRegistrars.orderedStream().forEach(resolver -> {
51-
resolver.register(registry);
52-
});
53-
commandRegistryCustomizers.orderedStream().forEach(customizer -> {
54-
customizer.customize(registry);
55-
});
56-
return registry;
57-
}
58-
59-
@Bean
60-
public CommandRegistryCustomizer defaultCommandRegistryCustomizer(
61-
ObjectProvider<CommandRegistration> commandRegistrations) {
62-
return registry -> {
63-
commandRegistrations.orderedStream().forEach(registration -> {
64-
registry.register(registration);
65-
});
66-
};
67-
}
45+
private static final Log log = LogFactory.getLog(SpringShellAutoConfiguration.class);
6846

6947
@Bean
70-
public CommandRegistrationCustomizer helpOptionsCommandRegistrationCustomizer(SpringShellProperties properties) {
71-
return registration -> {
72-
Help help = properties.getHelp();
73-
if (help.isEnabled()) {
74-
registration.withHelpOptions()
75-
.enabled(true)
76-
.longNames(help.getLongNames())
77-
.shortNames(help.getShortNames())
78-
.command(help.getCommand());
79-
}
80-
};
48+
@ConditionalOnMissingBean
49+
CommandRegistry commandRegistry(ApplicationContext applicationContext) {
50+
CommandRegistry commandRegistry = new CommandRegistry();
51+
registerProgrammaticCommands(applicationContext, commandRegistry);
52+
registerAnnotatedCommands(applicationContext, commandRegistry);
53+
return commandRegistry;
8154
}
8255

83-
@Bean
84-
@ConditionalOnBean(OptionNameModifier.class)
85-
public CommandRegistrationCustomizer customOptionNameModifierCommandRegistrationCustomizer(
86-
OptionNameModifier modifier) {
87-
return builder -> {
88-
builder.defaultOptionNameModifier(modifier);
89-
};
56+
private static void registerProgrammaticCommands(ApplicationContext applicationContext,
57+
CommandRegistry commandRegistry) {
58+
Map<String, Command> commandBeans = applicationContext.getBeansOfType(Command.class);
59+
commandBeans.values().forEach(commandRegistry::registerCommand);
9060
}
9161

92-
@Bean
93-
@ConditionalOnMissingBean(OptionNameModifier.class)
94-
@ConditionalOnProperty(prefix = "spring.shell.option.naming", name = "case-type")
95-
public CommandRegistrationCustomizer defaultOptionNameModifierCommandRegistrationCustomizer(
96-
SpringShellProperties properties) {
97-
return builder -> {
98-
switch (properties.getOption().getNaming().getCaseType()) {
99-
case NOOP:
100-
break;
101-
case CAMEL:
102-
builder.defaultOptionNameModifier(OptionNameModifierSupport.CAMELCASE);
103-
break;
104-
case SNAKE:
105-
builder.defaultOptionNameModifier(OptionNameModifierSupport.SNAKECASE);
106-
break;
107-
case KEBAB:
108-
builder.defaultOptionNameModifier(OptionNameModifierSupport.KEBABCASE);
109-
break;
110-
case PASCAL:
111-
builder.defaultOptionNameModifier(OptionNameModifierSupport.PASCALCASE);
112-
break;
113-
default:
114-
break;
62+
private static void registerAnnotatedCommands(ApplicationContext applicationContext,
63+
CommandRegistry commandRegistry) {
64+
Map<String, Object> springBootApps = applicationContext.getBeansWithAnnotation(SpringBootApplication.class);
65+
Class<?> mainClass = AopUtils.getTargetClass(springBootApps.values().iterator().next());
66+
String mainPackage = ClassUtils.getPackageName(mainClass);
67+
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(true);
68+
Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(mainPackage);
69+
for (BeanDefinition candidateComponent : candidateComponents) {
70+
String className = candidateComponent.getBeanClassName();
71+
if (className == null) {
72+
log.warn(String.format("Skipping candidate component %s with null class name", candidateComponent));
73+
continue;
11574
}
116-
};
117-
}
118-
119-
@Bean
120-
@ConditionalOnMissingBean
121-
public BuilderSupplier commandRegistrationBuilderSupplier(
122-
ObjectProvider<CommandRegistrationCustomizer> customizerProvider) {
123-
return () -> {
124-
CommandRegistration.Builder builder = CommandRegistration.builder();
125-
customizerProvider.orderedStream().forEach((customizer) -> customizer.customize(builder));
126-
return builder;
127-
};
75+
try {
76+
Class<?> cls = ClassUtils.forName(className, applicationContext.getClassLoader());
77+
ReflectionUtils.MethodFilter filter = method -> AnnotatedElementUtils.hasAnnotation(method,
78+
org.springframework.shell.core.command.annotation.Command.class);
79+
Set<Method> methods = MethodIntrospector.selectMethods(cls, filter);
80+
for (Method method : methods) {
81+
CommandFactoryBean factoryBean = new CommandFactoryBean(method);
82+
factoryBean.setApplicationContext(applicationContext);
83+
commandRegistry.registerCommand(factoryBean.getObject());
84+
}
85+
}
86+
catch (ClassNotFoundException e) {
87+
throw new ShellConfigurationException("Unable to configure commands from class " + className, e);
88+
}
89+
}
12890
}
12991

13092
}

spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CompleterAutoConfiguration.java

Lines changed: 0 additions & 87 deletions
This file was deleted.

0 commit comments

Comments
 (0)