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
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.
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.
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);
}
}
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.
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);
}
}