Picocli Spring boot CLI Application - spring-boot

I am trying to create a spring boot CLI App using picocli. I followed the steps as mentioned in the tutorial, but when I start the service the whole flow runs.
What I want is to call the command from the terminal then only the flow should trigger.Can anyone please help me resolving this.
Below is my code.
Component class
public class AppCLI implements CommandLineRunner {
#Autowired
AppCLI appCLI;
public String hello(){
return "hello";
}
#CommandLine.Command(name = "command")
public void command() {
System.out.println("Adding some files to the staging area");
}
#Override
public void run(String... args) throws Exception {
CommandLine commandLine = new CommandLine(appCLI);
commandLine.parseWithHandler(new CommandLine.RunLast(), args);
System.out.println("In the main method");
hello();
command();
}
}
Command class
#Controller
#CommandLine.Command(name = "xyz",description = "Performs ", mixinStandardHelpOptions = true, version = "1.0")
public class AppCLI implements Runnable{
#Override
public void run() {
System.out.println("Hello");
}
}
Main Class
#SpringBootApplication
public class Application {
private static Logger LOG = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Thanks in advance.

If you want to add command line parsing to Spring Boot's external
configuration in the #SpringBootApplication, do something like (see
Connect.java):
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine;
#SpringBootApplication
#Command
#NoArgsConstructor #ToString #Log4j2
public class Connect implements ApplicationRunner {
#Option(description = { "connection_file" }, names = { "-f" }, arity = "1")
#Value("${connection-file:#{null}}")
private String connection_file = null;
#Override
public void run(ApplicationArguments arguments) throws Exception {
new CommandLine(this)
.setCaseInsensitiveEnumValuesAllowed(true)
.parseArgs(arguments.getNonOptionArgs().toArray(new String [] { }));
/*
* Command implementation; command completes when this method
* completes.
*/
}
}
There is a similar example in
Install.java.

Related

SpringBootTest - how to assert if context loading fails

I wrote an ApplicationListener that should check if the environment is prepared during context initialization. I'm having trouble testing the scenario since I'm adding the listener manually both in my configure() and main() methods.
ApplicationListener class:
public class EnvironmentPrepared implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
#Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
//code that checks if conditions are met
if (checkTrue) {
throw new RuntimeException();
}
}
}
Main class:
public class MyApp extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
setRegisterErrorPageFilter(false);
return application.listeners(new EnvironmentPrepared()).sources(MyApp.class);
}
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MyApp.class);
springApplication.addListeners(new EnvironmentPrepared());
springApplication.run(args);
}
}
The test I want to execute:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ContextConfiguration(loader = OverriddenProfilesTest.CustomLoader.class)
public class OverriddenProfilesTest {
public static class CustomLoader extends SpringBootContextLoader {
#Override
protected SpringApplication getSpringApplication() {
SpringApplication app = super.getSpringApplication();
app.addListeners(new EnvironmentPrepared());
return app;
}
}
/**
* Checks if spring can bootstrap everything
*/
#Test(expected = RuntimeException.class)
public void test() {
}
}
This would be the test I want. A RuntimeException is thrown but the exception happens during context initialization so the test doesn't even start.
Here is the solution I used. I removed the manual adding of the listener to the application and used spring.factories file instead.
Regarding the test, I first created a custom runner class:
public class SpringRunnerWithExpectedExceptionRule extends SpringJUnit4ClassRunner {
public SpringRunnerWithExpectedExceptionRule(Class<?> clazz) throws InitializationError {
super(clazz);
}
#Override
protected Statement methodBlock(FrameworkMethod frameworkMethod) {
List<ExpectedException> testRules = getTestClass().getAnnotatedFieldValues(null, ExpectedExceptionClassRule.class, ExpectedException.class);
Statement result = super.methodBlock(frameworkMethod);
for (TestRule item : testRules) {
result = item.apply(result, getDescription());
}
return result;
}}
Then I create following annotation:
#Retention(RUNTIME)
#Target({ FIELD })
public #interface ExpectedExceptionClassRule {
}
And finally, I was able to run the test with my runner:
#RunWith(SpringRunnerWithExpectedExceptionRule.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class OverriddenProfilesTest {
#ExpectedExceptionClassRule
public static ExpectedException expectedException = ExpectedException.none();
#BeforeClass
public static void before() {
expectedException.expectCause(runtimeExceptionMethod());
}
#Test
public void testThatShouldThrowExceptionWhileSettingContext {
}
static Matcher<Throwable> runtimeExceptionMethod() {
return new IsRuntimeException();
}
static class IsRuntimeException extends TypeSafeMatcher<Throwable> {
//do stuff
}
More on the solution can be found here.

Spring boot - autowire fails

I'm trying to fetch some properties from application.properties file ad my code is the following:
Main Application class:
package it.mysite;
#SpringBootApplication
#EnableTransactionManagement
public class MailSenderApplication {
public static void main(String[] args){
ConfigurableApplicationContext context = SpringApplication.run(MailSenderApplication.class, args);
System.out.println("*****");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println("*****");
new MySendService().sendReport();
}
}
My service class:
package it.mysite.service;
#Service
public class MySendService {
#Value("${mail.fc.to}")
private String[] to;
#Value("${mail.fc.subject}")
private String subject;
#Autowired ReportService reportEmailService;
#Autowired MailProperties mailProperties;
public void sendReport(){
if(mailProperties.getTo().length > 0) {
}
}
Class where I fetch the properties:
package it.mysite.properties;
#Component
#ConfigurationProperties("mail.fc")
public class MailProperties {
private String[] to;
public String[] getTo(){
return to;
}
}
Config file:
# Email config
mail.fc.to=my#mail.com
mail.fc.subject=My subject
All of the #Autowired properties are null, and also the #Value properties (I tried to get them in that way also). When I print my context I can see these classes in the bean list, and for what I know my packages hierarchy is correct, so what can be the problem?
EDIT
Ok, I got the suggestion from the duplicate question and I changed my main class code as follows:
Main Application class:
package it.mysite;
#SpringBootApplication
#EnableTransactionManagement
public class MailSenderApplication {
#Autowired MySendService mySendService;
public static void main(String[] args){
ConfigurableApplicationContext context = SpringApplication.run(MailSenderApplication.class, args);
System.out.println("*****");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println("*****");
new MailSenderApplication().boot();
}
private void boot(){
mySendService.sendReport();
}
}
But I got the same error. Wasn't that the suggestion?

How can I Spring-ify my stand-alone (non Web Service) application?

I have a working stand-alone application, which uses Postgres, with the following
rough structure:
class myClass {
public myClass( String filePath ) {...}
public void calculate( ...args... ) {...}
public static void main(String[] args) {
...process args...
new myClass(...).calculate(...)
}
}
I am trying to convert this to a Spring Boot application to take advantage of
Spring JDBC. Here's the rough structure, a modification of the above:
#SpringBootApplication
class myClass implements implements CommandLineRunner {
public myClass( String filePath ) {...}
public void calculate( ...args... ) {...}
public static void main(String[] args) {
SpringApplication.run( myClass.class, args);
}
#Override
public void run(String... args) throws Exception {
...process args...
new myClass(...).calculate(...)
}
}
When I try running this from Eclipse, I get the following error message:
Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
Why is this happening, what can I do to fix it? And, why is it even complaining about "WebApplication",
since I do not include anything having to do with Web or Controller in my build.gradle.
I was able to find a solution to the error above by slightly modifying how I structured my main method:
#SpringBootApplication
class myClass implements implements CommandLineRunner {
public myClass( String filePath ) {...}
public void calculate( ...args... ) {...}
public static void main(String[] args) {
SpringApplication application = new SpringApplication( myClass.class );
application.setWebEnvironment( false );
application.run( args );
}
#Override
public void run(String... args) throws Exception {
...process args...
this.calculate(...) <<<=== Note change here, too
}
}

Spring Boot + DynamoDBTypeConverter dependancy injection

I'm trying to get DI working with a sample DynamoDBTypeConverter I'm playing around with and having no luck at all :( My service is always null and throws an error as a result in my jUnit test.
Here's my converter:
#Component
public class ArmTypeConverter implements DynamoDBTypeConverter<String, Arm> {
#Autowired
private ArmRepository armRepository;
#Override
public String convert(Arm Arm) {
return arm.getId();
}
#Override
public Arm unconvert(String id) {
return armRepository.findOne(id);
}
}
My application main:
#SpringBootApplication
#ComponentScan
#EnableSpringConfigured
#EnableLoadTimeWeaving(aspectjWeaving=EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
#EnableAutoConfiguration
public class ArmApplication implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(ArmApplication.class, args);
}
#Bean
public InstrumentationLoadTimeWeaver loadTimeWeaver() throws Throwable {
InstrumentationLoadTimeWeaver loadTimeWeaver = new InstrumentationLoadTimeWeaver();
return loadTimeWeaver;
}
}
My service:
#Service
public class ArmServiceImpl implements ArmService {
#Autowired
private ArmRepository armRepository;
#Override
public Arm create(String length, Set<Register> registers) {
Date now = new Date();
Arm arm = new Arm();
arm.setLength("85cm");
return armRepository.save(arm);
}
}
My Test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class ArmServiceTests {
#Autowired
private ArmService armService;
#Autowired
private TorsoService torsoService;
private Arm arm;
#Before
public void before() {
arm = armService.create("85cm", null);
torsoService.create("150cm", arm);
}
#After
public void after() {
// do nothing for now...
}
#Test
#WithUserDetails("admin#somewhere.com")
public void getArmTest() {
Arm c = armService.getArm(arm.getId());
assertThat(c).isNotNull();
assertThat(c.getId()).isEqualTo(arm.getId());
}
}
What am I doing wrong?
The issue was that I didn't have load time weaving configured properly

Spring boot check external service status on boot

I want check some external http service before my Spring Boot is ready.
The url to the external web service are stored in a property file with a #ConfigurationProperties class.
How do this check i tried using a springApplication.addListner() with a ping method. But the property class have not then been initialized.
public class ApplicationStartListener implements ApplicationListener<ApplicationPreparedEvent> {
#Override
public void onApplicationEvent(ApplicationPreparedEvent event) {
String url = AppProp.getURL();
inet = InetAddress.getByName(url );
inet.isReachable(5000)
...
application.yml
tops:
http://service.com
#Component
#ConfigurationProperties("tops")
public class AppProp{
private static String url;
public static String getUrl() {
The easiest way to accomplish this is to implement the ApplicationRunner interface.
From the Spring Boot documentation [1]
If you need to run some specific code once the SpringApplication has started, you can implement the ApplicationRunner or CommandLineRunner interfaces. Both interfaces work in the same way and offer a single run method which will be called just before SpringApplication.run(…​) completes.
[1] https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html#boot-features-command-line-runner
Assuming you have url defined in application.properties:
#SpringBootApplication
public class MyApplication implements ApplicationRunner
{
#Inject
private AppConfig appConfig;
#Inject
private ConfigurableApplicationContext applicationContext;
public static void main(String[] args)
{
SpringApplication.run(MyApplication.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception
{
InetAddress inetAddress = InetAddress.getByName(appConfig.getUrl());
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Component
#ConfigurationProperties
public static class AppConfig
{
private String url;
public String getUrl()
{
return url;
}
public void setUrl(String url)
{
this.url = url;
}
}
}
If you need even more control than this, you can use SpringApplicationRunListener [2]
[2] http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/SpringApplicationRunListener.html
#SpringBootApplication
public class MyApplication implements SpringApplicationRunListener
{
public MyApplication() { }
public MyApplication(SpringApplication springApplication, String[] args) { }
public static void main(String[] args)
{
SpringApplication.run(MyApplication.class, args);
}
#Override
public void started() { }
#Override
public void environmentPrepared(ConfigurableEnvironment environment)
{
// 1st opportunity
InetAddress inetAddress = InetAddress.getByName(environment.getProperty("url"));
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Override
public void contextPrepared(ConfigurableApplicationContext context)
{
// 2nd opportunity
InetAddress inetAddress = InetAddress.getByName(context.getEnvironment().getProperty("url"));
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Override
public void contextLoaded(ConfigurableApplicationContext context)
{
// 3rd opportunity
InetAddress inetAddress = InetAddress.getByName(context.getEnvironment().getProperty("url"));
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Override
public void finished(ConfigurableApplicationContext context, Throwable exception)
{
// 4th opportunity
InetAddress inetAddress = InetAddress.getByName(context.getEnvironment().getProperty("url"));
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Component
#ConfigurationProperties
public static class AppConfig {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
}
then create META-INF\spring.factories and add
org.springframework.boot.SpringApplicationRunListener=com.foobar.MyApplication

Resources