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

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

Related

Is there a way to use Spring boot beans in a Gatling simulation?

I am currently writing a Spring boot application that will perform loadtests on another app. I want to use Gatling to manage the tests, but I need it to access the configuration that I defined in beans of my Spring app.
Here is what I would like to see working :
public class MySimulation extends Simulation {
#Autowired
private JMSConnectionFactoryBeanClass myConnectionFactory;
public MySimulation() {
JmsProtocolBuilder jmsProtocol = jms.connectionFactory(myBean);
ScenarioBuilder scn = scenario("My Simulation Scenario")
.exec(
jms("test")
.send()
.queue("myQueue")
.textMessage("message")
);
{
setUp(
scn.injectOpen(rampUsers(10).during(5))
).protocols(jmsProtocol);
}
}
When I hardcode the configuration into the simulation class and remove all #Autowired thing, everything works, so it must be comming from the dependency injection. Does anybody know if there is a way to us spring beans in a gatling simulation ?
Following Stéphane Landelle advice, here is what I came up with, but instead of creating my app context inside of the simulation, I figured out how to run the simulation along with my spring app using gatling API :
public class GatlingRunner {
public static void run() {
GatlingPropertiesBuilder props = new GatlingPropertiesBuilder();
props.simulationClass("path.to.Simulation");
Gatling.fromMap(props.build());
}
}
This is how I modified my spring app :
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
GatlingRunner.run();
}
}
Finally, to use spring beans in the simulation, I wrote a context provider that would make the link between spring and gatling :
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext context)
throws BeansException {
ApplicationContextProvider.context = context;
}
}
Now, to get a bean inside of the simulation, all I needed was this :
Bean myBean = ApplicationContextProvider.getApplicationContext()
.getBean("myBean", Bean.class)
You can't use #Autowired. You have to create an ApplicationContext programmatically and pull the JMSConnectionFactoryBeanClass from it.
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = MySimulation.class, loader = SpringApplicationContextLoader.class)
public class MySimulation extends GatlingTest {
#Autowired
private JMSConnectionFactoryBeanClass myConnectionFactory;
#Test
public void test() {
...
}
}

How to execute code in a SpringBootTest before the Application is run?

I have a SpringBoot based command line application. The application creates or deletes some records in a database. It does so not directly via JDBC but rather through a special API (instance variable dbService).
The application class looks like this:
#SpringBootApplication
public class Application implements CommandLineRunner {
#Autowired
private DbService dbService;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... args) {
// Do something via dbService based on the spring properties
}
}
Now I'd like to create a SpringBoot test that would run the whole application with a configuration specially prepared for the test.
I run the test with an in-memory DB (H2) which is empty at the test start. Hence I'd like to insert some records into the DB -- as the setup for the test. The code for inserting the records must be executed
After the Spring context has been loaded -- so that I can use the bean dbService.
Before the Application is run -- so that the application runs with the prepared DB.
Somehow I fail to implement the above two points.
What I have so far is this:
#SpringBootTest
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
#ActiveProfiles("specialtest")
public class MyAppTest {
#Autowired
private DbService dbService;
private static final Logger logger = LoggerFactory.getLogger(MyAppTest.class);
// The expectation is that this method is executed after the spring context
// has been loaded and all beans created, but before the Application class
// is executed.
#EventListener(ApplicationStartedEvent.class)
public void preparedDbForTheTest() {
// Create some records via dbService
logger.info("Created records for the test");
}
// This test is executed after the application has run. Here we check
// whether the DB contains the expected records.
#Test
public void testApplication() {
// Check the DB contents
}
}
My problem is that the the method preparedDbForTheTest does not seem to get executed at all.
According to the SpringBoot docs, the event ApplicationReadyEvent is sent exactly when I want to execute the setup code. But somehow the code is not executed.
If I annotate the method with #Before... (I tried several variants of it) then it gets executed, but after the Application class has run.
What am I doing wrong?
Test classes aren't Spring-managed beans so things like #EventListener methods will be ignored.
The most conventional solution to your problem would be to add some #TestConfiguration that declares the #EventListener:
#SpringBootTest
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
public class MyAppTest {
private static final Logger logger = LoggerFactory.getLogger(MyAppTest.class);
#Test
public void testApplication() {
}
#TestConfiguration
static class DatabasePreparation {
#EventListener(ApplicationStartedEvent.class)
public void preparedDbForTheTest() {
logger.info("Created records for the test");
}
}
}
A #TestConfiguration is additive so it'll be used alongside your application's main configuration. The preparedDbForTheTest method will now be called as part of refreshing the application context for the tests.
Note that, due to application context caching, this method won't be called for every test. It will only be called as part of refreshing the context which may then be shared among several tests.

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.

Alfresco Process Services with TaskListener #Autowired issue

I am using Alfresco Process Services and have created a created a spring boot project for custom logic like TaskListeners and Delegations. I am creating the jar file from this maven project and copying it into webapps/activiti-app/WEB-INF/lib folder.
I have a simple TaskListener as below which is getting called on Task start. But the #Autowired variables are always null.
package com.activiti.extension.bean;
#Component("myTaskListener")
public class MyTaskListener implements TaskListener {
#Autowired
UserService userService;
#Override
public void notify(DelegateTask task) {
logger.info("userService: " +userService); // Always prints null
}
Finally I was able to make it work. I was putting the task listener in the class field of the Task properties with full package name. Now I am putting Delegate expression like ${myTaskListener} and it worked...
Thank you all for your time and help
This is because your your MyTaskListener is annotated as #Component or at least being ignored by spring during init. for auto-wiring capabilities spring requires this annotation (or similar to this) under the provided #ComponentScan packages to consider the class as a bean otherwise it will take as a normal java class and hence the #autowired is of no use in your case.
This below code is worked for me
#Component
public class MyTaskListener implements TaskListener {
public static UserService getUserServiceObject() {
return SpringApplicationContextHolder.getApplicationContext().getBean(UserService.class);
}
#Override
public void notify(DelegateTask delegateTask) {
//UserService Object, It is not null now
getUserServiceObject();
}
}
#Component
public class SpringApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
There is also one more way to get to your custom service "UserService" using Alfresco Spring Application context.
First access ServiceRegistry (registry used for accessing Alfresco Services but also any other custom service):
ServiceRegistry serviceRegistry = (ServiceRegistry) Context.getProcessEngineConfiguration().getBeans().get(ActivitiConstants.SERVICE_REGISTRY_BEAN_KEY);
Then get custom service UserService:
QName qname = QName.createQName("UserService");
UserService userService = (UserService) serviceRegistry.getService(qname);

Why do I need to add #Component when I have to schedule the task in spring boot

I didn't understand the proper use of #Componenet, #Configuration,#Bean annotation.
I want to run one method in every 60 seconds.Please check the below code. If I don't give #Component annotation then it doesn't run. so What is the use of #Component in this context?
#EnableScheduling
public class SchedulingProjectApplication {
private static final Logger log =
LoggerFactory.getLogger(SchedulingProjectApplication.class);
public static void main(String[] args) {
SpringApplication.run(SchedulingProjectApplication.class, args);
}
#Scheduled(fixedDelay = 6000)
public void r()
{
log.info("Start- main-job");
log.info("stop-main-job");
}
}
There are several problems with this piece of code:
Your Spring Boot application is not flagged with #SpringBootApplication (or #EnableAutoConfiguration). As a result, the auto-configuration will not kick in at all (Spring Boot will start your app but won't do anything with it besides basic stuff such as env preparation, etc). It's perfectly fine in certains cases but that isn't probably what you want
You've flagged the run task directly on your app. It's ok for demo but it would be better to move that logic in its own class
So to answer your question: SchedulingProjectApplication is the root source of your app but it's just a simple POJO. There's nothing that instructs the container to process it. Usually the app is a #Configuration (you can use one of the #EnableXYZ on it, you can define additional beans, etc.
If you add #SpringBootApplication on your class, it will scan any #Component in the same package of your app (and all the sub-packages).
More details about code structure in the documentation
One basic/simple structure for you would be:
package com.example.foo;
#SpringBootApplication
#EnableScheduling
public class SchedulingProjectApplication {
public static void main(String[] args) {
SpringApplication.run(SchedulingProjectApplication.class, args);
}
}
And
package com.example.foo;
#Component
public class SchedulingLogger {
private static final Logger log =
LoggerFactory.getLogger(SchedulingLogger.class);
#Scheduled(fixedDelay = 6000)
public void r()
{
log.info("Start- main-job");
log.info("stop-main-job");
}
}
There are other things that you should be aware with regards to configuration (such as moving decisions outside of your #SpringBootApplication if you use slicing).

Resources