diff --git a/README.md b/README.md
index 5e5a6ce..9b41d74 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,9 @@
[](https://travis-ci.org/kakawait/picocli-spring-boot-starter)
[](https://sonarcloud.io/component_measures?id=com.kakawait%3Apicocli-spring-boot-starter-parent&metric=coverage)
-[](https://search.maven.org/#artifactdetails%7Ccom.kakawait%7Cpicocli-spring-boot-starter%7C0.2.0%7Cjar)
+[](https://search.maven.org/#artifactdetails%7Ccom.kakawait%7Cpicocli-spring-boot-starter%7C1.0.0-beta-1%7Cjar)
[](https://github.com/kakawait/picocli-spring-boot-starter/blob/master/LICENSE.md)
+[](https://twitter.com/intent/follow?screen_name=thibaudlepretre)
> A Spring boot starter for [Picocli](http://picocli.info/) command line tools. That let you easily write CLI for Spring boot application!
@@ -24,7 +25,7 @@ Add the Spring boot starter to your project
com.kakawait
picocli-spring-boot-starter
- 0.2.0
+ 1.0.0-beta-1
```
@@ -116,9 +117,66 @@ But the following example will not be candidate for _Main_ command
class MainCommand {}
```
-#### Nested sub-commands using beans
+#### Nested sub-commands
-Picocli allows [_nested sub-commands_](http://picocli.info/#_nested_sub_subcommands), in order to describe a _nested sub-command_, starter is offering you nested classes scanning capability.
+Picocli allows [_nested sub-commands_](http://picocli.info/#_nested_sub_subcommands), in order to describe a _nested sub-command_, starter is offering two ways to describe your structure.
+
+Please refer to next points to see how to construct this following command line application using both way:
+
+```
+Commands:
+ flyway [-h, --help]
+ migrate
+ repair
+```
+
+`java -jar .jar flyway migrate` will execute _Flyway_ migration.
+
+You can mix methods but is not recommended!
+
+##### Using `subCommands` from `@Command` annotation
+
+```java
+@Component
+@Command(name = "flyway", subCommands = { MigrateCommand.class, RepairCommand.class })
+class FlywayCommand extends HelpAwareContainerPicocliCommand {}
+
+@Component
+@Command(name = "migrate")
+class MigrateCommand implements Runnable {
+
+ private final Flyway flyway;
+
+ public MigrateCommand(Flyway flyway) {
+ this.flyway = flyway;
+ }
+
+ @Override
+ public void run() {
+ flyway.migrate();
+ }
+}
+
+@Component
+@Command(name = "repair")
+class RepairCommand implements Runnable {
+ private final Flyway flyway;
+
+ public RepairCommand(Flyway flyway) {
+ this.flyway = flyway;
+ }
+
+ @Override
+ public void run() {
+ flyway.repair();
+ }
+}
+```
+
+By default starter is providing a custom implementation [`ApplicationContextAwarePicocliFactory`](picocli-spring-boot-autoconfigure/src/main/java/com/kakawait/spring/boot/picocli/autoconfigure/ApplicationContextAwarePicocliFactory.java) of [`CommandLine.IFactory`](https://picocli.info/apidocs/picocli/CommandLine.IFactory.html) that will delegate instance creation to _Spring_ `ApplicationContext` in order to load bean if exists.
+**ATTENTION** If subCommand is not a defined bean, [`ApplicationContextAwarePicocliFactory`](picocli-spring-boot-autoconfigure/src/main/java/com/kakawait/spring/boot/picocli/autoconfigure/ApplicationContextAwarePicocliFactory.java) will only instanciate class without any _autowiring_ capability.
+
+##### Using java nested class hierarchy
That means, if you're defining **bean** structure like following:
@@ -160,20 +218,9 @@ class FlywayCommand extends HelpAwareContainerPicocliCommand {
}
```
-Will generate command line
-
-```
-Commands:
- flyway [-h, --help]
- migrate
- repair
-```
-
-Thus `java -jar .jar flyway migrate` will execute _Flyway_ migration.
-
**ATTENTION** every classes must be a bean (`@Component`) with `@Command` annotation without forgetting to file `name` attribute.
-There is **no limitation** about nesting level.
+Otherwise, there is **no limitation** about nesting level.
### Additional configuration
diff --git a/picocli-spring-boot-autoconfigure/pom.xml b/picocli-spring-boot-autoconfigure/pom.xml
index 4393c77..7d83500 100644
--- a/picocli-spring-boot-autoconfigure/pom.xml
+++ b/picocli-spring-boot-autoconfigure/pom.xml
@@ -5,7 +5,7 @@
4.0.0
com.kakawait
picocli-spring-boot-autoconfigure
- 0.2.0
+ 1.0.0-beta-1
jar
Picocli spring boot autoconfigure
@@ -41,17 +41,17 @@
1.8
1.8
- 1.5.4.RELEASE
+ 2.1.4.RELEASE
- 0.9.8
+ 3.9.6
1.7.25
- 3.8.0
+ 3.12.2
2.0.0.0
- 2.15.0
+ 2.27.0
3.0.1
- 2.10.4
+ 3.0.1
1.6
1.6.8
@@ -81,7 +81,6 @@
org.slf4j
slf4j-api
- ${slf4j-api.version}
@@ -95,7 +94,16 @@
junit
test
-
+
+ ch.qos.logback
+ logback-classic
+ test
+
+
+ ch.qos.logback
+ logback-core
+ test
+
org.assertj
assertj-core
@@ -142,7 +150,7 @@
release
- gpg2
+ gpg
diff --git a/picocli-spring-boot-autoconfigure/src/main/java/com/kakawait/spring/boot/picocli/autoconfigure/ApplicationContextAwarePicocliFactory.java b/picocli-spring-boot-autoconfigure/src/main/java/com/kakawait/spring/boot/picocli/autoconfigure/ApplicationContextAwarePicocliFactory.java
new file mode 100644
index 0000000..c5e8a9b
--- /dev/null
+++ b/picocli-spring-boot-autoconfigure/src/main/java/com/kakawait/spring/boot/picocli/autoconfigure/ApplicationContextAwarePicocliFactory.java
@@ -0,0 +1,37 @@
+package com.kakawait.spring.boot.picocli.autoconfigure;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+import picocli.CommandLine;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * @author Thibaud Leprêtre
+ */
+public class ApplicationContextAwarePicocliFactory implements CommandLine.IFactory {
+ private static final Logger logger = LoggerFactory.getLogger(ApplicationContextAwarePicocliFactory.class);
+
+ private final ApplicationContext applicationContext;
+
+ public ApplicationContextAwarePicocliFactory(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ @Override
+ public K create(Class aClass) throws Exception {
+ try {
+ return applicationContext.getBean(aClass);
+ } catch (Exception e) {
+ logger.info("unable to get bean of class {}, use standard factory creation", aClass);
+ try {
+ return aClass.newInstance();
+ } catch (Exception ex) {
+ Constructor constructor = aClass.getDeclaredConstructor();
+ constructor.setAccessible(true);
+ return constructor.newInstance();
+ }
+ }
+ }
+}
diff --git a/picocli-spring-boot-autoconfigure/src/main/java/com/kakawait/spring/boot/picocli/autoconfigure/PicocliAutoConfiguration.java b/picocli-spring-boot-autoconfigure/src/main/java/com/kakawait/spring/boot/picocli/autoconfigure/PicocliAutoConfiguration.java
index aa2ff17..578c27e 100644
--- a/picocli-spring-boot-autoconfigure/src/main/java/com/kakawait/spring/boot/picocli/autoconfigure/PicocliAutoConfiguration.java
+++ b/picocli-spring-boot-autoconfigure/src/main/java/com/kakawait/spring/boot/picocli/autoconfigure/PicocliAutoConfiguration.java
@@ -1,10 +1,20 @@
package com.kakawait.spring.boot.picocli.autoconfigure;
+import java.lang.reflect.Method;
+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 java.util.Objects;
+import java.util.Optional;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.boot.CommandLineRunner;
-import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -20,18 +30,9 @@
import org.springframework.context.annotation.Import;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ReflectionUtils;
-import picocli.CommandLine;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-import static picocli.CommandLine.Command;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
/**
* @author Thibaud Leprêtre
@@ -41,6 +42,12 @@
@Import(PicocliAutoConfiguration.CommandlineConfiguration.class)
class PicocliAutoConfiguration {
+ @Bean
+ @ConditionalOnMissingBean(CommandLine.IFactory.class)
+ CommandLine.IFactory applicationAwarePicocliFactory(ApplicationContext applicationContext) {
+ return new ApplicationContextAwarePicocliFactory(applicationContext);
+ }
+
@Bean
@ConditionalOnMissingBean(PicocliCommandLineRunner.class)
@ConditionalOnBean(CommandLine.class)
@@ -52,7 +59,13 @@ CommandLineRunner picocliCommandLineRunner(CommandLine cli) {
@Conditional(CommandCondition.class)
static class CommandlineConfiguration {
- private final Logger logger = LoggerFactory.getLogger(CommandlineConfiguration.class);
+ private static final Logger logger = LoggerFactory.getLogger(CommandlineConfiguration.class);
+
+ private final CommandLine.IFactory applicationAwarePicocliFactory;
+
+ public CommandlineConfiguration(CommandLine.IFactory applicationAwarePicocliFactory) {
+ this.applicationAwarePicocliFactory = applicationAwarePicocliFactory;
+ }
@Bean
CommandLine picocliCommandLine(ApplicationContext applicationContext) {
@@ -60,11 +73,11 @@ CommandLine picocliCommandLine(ApplicationContext applicationContext) {
List