How to bootstrap SpringBoot application (without mvc)? - spring

I want to use spring-data & IOC container for some test app.
And the problem is: how to bootstrap the app?
In case of spring-mvc we move from controllers, but how to do this without mvc?
I need some main-like method for my code, but the application public static void main is already used for spring initialization:
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
So, where should I place my code?

There is a CommandLineRunner interface that just means to do that.
#Service
class FooBar implements CommandLineRunner {
// Inject whatever collaborator you need
#Override
void run(String... args) throws Exception {
// Execute whatever you need.
// command-line arguments are available if you need them
}
}

Related

How to make some setup work before ApplicationEvent listener in test

I have a customized spring-boot-starter which will call some REST APIs when it gets a spring application event of ApplicationReadyEvent, so the configuration class is something like:
#Configuration
public class MySpringBootStarter {
#EventListener(ApplicationReadyEvent.class)
public void init() {
// Call REST APIs here
}
}
Then, I want to test the starter using MockServer which requires creating some expectations before the test runs. The test class may look like as follows:
#ExtendWith(MockServerExtension.class)
#SpringBootTest
#ContextConfiguration
#MockServerSettings(ports = {28787, 28888})
public class MySpringBootStarterTest {
private MockServerClient client;
#BeforeEach
public void beforeEachLifecycleMethod(MockServerClient client) {
this.client = client;
//creating expectations here
}
#Test
void shouldBeTrue() {
assertThat(true).isTrue();
}
#SpringBootApplication
static class MyTest {
public void main(String[] args) {
SpringApplication.run(Test.class, args);
}
}
}
But in fact, the expectations are always created after the ApplicationReadyEvent, viz., the init method of MySpringBootStarter class is called before the the beforeEachLifecycleMethod method in MySpringBootStarterTest class.
How can I make the test work, please?
You can use static block initializer to run required code before SpringContext boots up.

Why is my Spring application run from my spring boot unit test

I have a basic spring data application and I have written a unit test. What appears to happen is that when I run the Spring test my application run method gets called as well. I would like to know why this is and how to stop it please.
I have tried using active profiles but that doesnt fix the problem
#SpringBootApplication
#EntityScan({ "com.demo" })
public class Application implements ApplicationRunner {
#Autowired
private IncrementalLoadRepository repo;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
IncrementalLoad incrementalLoad = new IncrementalLoad("fred", Instant.now(), Instant.now(), Instant.now());
repo.save(incrementalLoad);
}
and the unit test........
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { Application.class })
#ActiveProfiles("test")
public class IncrementalLoadServiceTest {
#Autowired
private IncrementalLoadService incrementalLoadService;
#Test
public void checkInitialRecords_incrementalLoad() {
List<IncrementalLoad> incrementalLoads = incrementalLoadService.list();
assertEquals(3, incrementalLoads.size());
}
So I think I found the solution. I created another #SpringBootApplication class in my test folders. Initially that failed but I believe thats because the entity scan annotation pointed to packages where my "production" #SpringBootApplication was. I moved that class up a level and it all seems to work ok now.

How can I run a specific class / utility in a Spring Boot application with wiring?

I have my standard Spring Boot application working. I have situations where I want to run a "job" which is basically some specific method normally run via a user doing something in their browser but I want to run it from command line.
I'm able to run an arbitrary class with gradlew;
./gradlew -PmainClass=kcentral.backingservices.URLMetaExtractor execute
However when run this way none of the "autowiring" works. What is a better way to execute an arbitrary class (that has a main method) such that it also works with any Autowiring?
EDIT:
I got some advice to use a CommandLineRunner and some args, which work to execute the command via:
./gradlew bootRun -Pargs=--reloadTestData
However, the Autowiring of my Repo is failing. What I have is:
#EnableAutoConfiguration
#EnableMongoAuditing
#EnableMongoRepositories(basePackageClasses=KCItemRepo.class)
#ComponentScan(basePackages = {"kcentral"})
public class ReloadTestData implements CommandLineRunner {
#Autowired
AddItemService addItemService;
#Autowired
KCItemRepo itemRepo;
#Autowired
KCItemRatingRepo itemRatingRepo;
private static final Logger log = LoggerFactory.getLogger(ReloadTestData.class);
public void reloadData(){
log.info("reloadData and called");
if (itemRepo == null){
log.error("Repo not found");
return;
}
long c = itemRepo.count();
log.warn("REMOVING ALL items "+c);
itemRepo.deleteAll();
log.warn("REMOVING ALL ratings");
itemRatingRepo.deleteAll();
}
itemRepo is always null even though I wire the same way in my 'regular' spring boot app without an issue. What do I need to do to have it wire properly?
The fact that you say you want to run a "job" suggests that you might want to use a scheduled task within your application, rather than trying to run it through the command line. e.g. Scheduling tasks in Spring
#Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
log.info("The time is now {}", dateFormat.format(new Date()));
}
If you want to make a command line application work with Autowiring, you can make a command line application by making your Application class implement the CommandLineRunner interface, e.g. Spring Boot Console App
#SpringBootApplication
public class SpringBootConsoleApplication
implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(SpringBootConsoleApplication.class, args);
}
#Override
public void run(String... args) {
}
}
And add spring.main.web-application-type=NONE to the properties file.
If you want to stop the application after running you can use SpringApplication.exit(ctx). Don't know about your auto-wiring problem though, maybe try printing out the list of available beans which might give some insight. Example:
#Component
public class DoThenQuit implements CommandLineRunner {
#Autowired
private ApplicationContext ctx;
#Override
public void run(String[] args) {
// do some other stuff before quitting
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.stream(beanNames).forEach(System.out::println);
// then quit the application
SpringApplication.exit(ctx);
}
}

spring boot - load app config before calling method run

is there a way to read yaml file of a spring boot app before launching the app, more exactly before calling method run.
here is my code :
#SpringBootApplication
public class CometeRestApi extends SpringBootServletInitializer {
private static void setMongoSecure() {
System.setProperty("javax.net.ssl.keyStore", System.getProperty("user.home") + "/.ssl/client-and-key.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "");
System.setProperty("javax.net.ssl.trustStore", System.getProperty("user.home") + "/.ssl/certificateChain.jks");
System.setProperty("javax.net.ssl.trustPassword", "");
}
#Override
protected SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
setMongoSecure();
return application.sources(CometeRestApi.class);
}
public static void main(final String[] args) {
setMongoSecure();
SpringApplication.run(CometeRestApi.class, args);
}
}
thanks in advance for help
Yes, there are several ways to do it, but the recommended way is to use an EventListener. Annotating a method with #EventListener(ApplicationReadyEvent.class) is ideal when your purpose is to automatically execute code after your application startup.

How to configure Spring Data REST to return the representation of the resource created for a POST request?

I am following the spring-data-rest guide Accessing JPA Data with REST. When I http post a new record it is inserted (and the response is a 201). That is great, but is there a way to configure the REST MVC code to return the newly created object? I'd rather not have to send a search request to find the new instance.
You don't have to search for the created entity. As the HTTP spec suggests, POST requests returning a status code of 201 Created are supposed to contain a Location header which contains the URI of the resource just created.
Thus all you need to do is effectively issuing a GET request to that particular URI. Spring Data REST also has two methods on RepositoryRestConfiguration.setReturnBodyOnCreate(…) and ….setReturnBodyOnUpdate(…) which you can use to configure the framework to immediately return the representation of the resource just created.
Example with Spring Boot:
#Configuration
#EnableMongoRepositories
#Import(RepositoryRestMvcConfiguration.class)
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
RepositoryRestConfiguration restConfiguration = ctx.getBean(RepositoryRestConfiguration.class);
restConfiguration.setReturnBodyOnCreate(true);
}
}
or
#Configuration
#EnableMongoRepositories
#EnableAutoConfiguration
public class Application extends RepositoryRestMvcConfiguration {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
super.configureRepositoryRestConfiguration(config);
config.setReturnBodyOnCreate(true);
}
}
Good Luck!
If you are using Spring Boot, you can add the following lines to your application.properties file for POST (create) and PUT (update) respectively
spring.data.rest.return-body-on-create=true
spring.data.rest.return-body-on-update=true
Here's another variant that uses DI rather than extending RepositoryRestMvcConfiguration or using the ConfigurableApplicationContext.
#SpringBootApplication
#EnableConfigurationProperties
#Configuration
#ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Autowired private RepositoryRestConfiguration repositoryRestConfiguration;
#PostConstruct
public void exposeIds() {
this.repositoryRestConfiguration.setReturnBodyForPutAndPost(true);
}
}

Resources