How to trigger a Spring cloud task from an external application? - spring-cloud-task

I have created a spring cloud task that will perform some specific task based on the requirement. I wanted to call this task from another spring boot application. Please let me know is there any way of calling the below task from an external application.
#SpringBootApplication
#EnableTask
public class FileGenerationTaskApplication {
#Autowired
private DataSource dataSource;
public class FileGeneratorTaskConfigurer extends DefaultTaskConfigurer {
public FileGeneratorTaskConfigurer(DataSource dataSource){
super(dataSource);
}
}
#Bean()
public FileGeneratorTaskConfigurer getTaskConfigurer() {
return new FileGeneratorTaskConfigurer(dataSource);
}
public static void main(String[] args) {
SpringApplication.run(FileGenerationTaskApplication.class, args);
}
#Component
public static class FileGeneratorTaskRunner implements ApplicationRunner {
#Autowired
private FulfillmentFileGenerationService service;
public void run(ApplicationArguments args) throws Exception {
System.out.println("FileGeneratorTaskRunner from Spring Cloud Task!");
service.fulFillmentFileGenerationTask();
}
}
}
Can we create a REST api to call the spring cloud task?

It would be nice to have the Task registered on Spring Cloud Dataflow.
After you have your Task registered, you can make REST calls to trigger the Task. Check this example out.
You can also use Spring Cloud Dataflow Rest Client
DataFlowOperations dataFlowOperations = new DataFlowTemplate(URI.create(springDataFlowUri));
TaskOperations operations = dataFlowOperations.taskOperations();
Then you can start launching the Tasks previously got using the API Rest.
In case you do not want to use Spring Cloud DataFlow, remember when you create a Task, this is a Spring Boot Application by itself, so you can expose end points to trigger the Task.

Related

How do I setup baggage fields in Spring Cloud Sleuth for a Command Line Runner?

I'm successfully using Spring Cloud Sleuth in a Spring Boot microservice and am having fields logged and sent over http headers appropriately.
I now need to integrate this same process for logging and header propagation in a Spring Boot command line runner application but it looks like no trace and span are automatically setup since it isn't in the middle of an Http request (as it is a command line app). I cannot see these fields in my logs (with the same %X format in log configuration).
I've looked at the docs and can't find any examples for this specific use case. Is this possible in a command line runner app?
In order to add baggage you need to have a span. Spring Cloud Sleuth and Spring Boot create a span for you when the controller is invoked. If you want to do the same using CLI application, you need to create span yourself.
You have two options.
Using API calls:
Span span = this.tracer.nextSpan().name("mySpan");
// do some work
span.end(); // best to put it in finally to make sure span is always ended
Or you can use annotations:
#NewSpan
public void doWork() {
}
If you use the annotation, please keep in mind the AOP proxies limitations. In particular self invocations (calls using this) would not work.
#SpringBootApplication
public class ConsoleApplication
implements CommandLineRunner {
#Override
public void run(String... args) {
doWork(); //this is the same as this.doWork();
}
#NewSpan
public void doWork() {
}
}
This is not going to work as doWork is not invoked through the AOP proxy. Make sure that you annotate a component managed by Spring and then use an injected instance.
#SpringBootApplication
public class ConsoleApplication
implements CommandLineRunner {
#Autowired
private MyService myService;
#Override
public void run(String... args) {
myService.doWork();
}
}
#Component
class MyService {
#NewSpan
public void doWork() {
}
}
In this case myService is not instance of MyService, but rather an instrumented proxy.

Run Spring Batch (JSR-352) application on Spring Boot

I have a simple Spring Batch application complying with JSR-352.
I need to deploy this as a managed Task on Spring Cloud Data Flow server. As far as I know - to be able to deploy this as a Task I need to convert this application as a Spring Boot app.
I have tried to add Spring Boot dependencies and Main class however it is not running the Batch job when I start the app.
Main Class
#SpringBootConfiguration
#EnableAutoConfiguration
#EnableBatchProcessing
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Batch File created at
META-INF/batch-jobs/myjob.xml
It works when I use JobOperator in the main class to start the job (without Spring Boot).
What am I missing to run this as a Spring Boot app?
You're missing #EnableTask annotation. With that, your batch-job will be run as a short-lived application. In other words, the application will run as long as the business logic in your XML needs to run, and it will gracefully shut down and free-up resources.
Please clone and try out the Spring Cloud Task samples [see: BatchJobApplication]. All of them should work as-is in SCDF as well.
#EnableBatchProcessing
#SpringBootApplication
public class BatchApplication {
public static void main(String[] args) {
SpringApplication.run(BatchApplication.class, args);
}
#Bean
public CommandLineRunner run(JobOperator jobOperator) {
return $ -> jobOperator.start("myjob", new Properties());
}
#Bean
JobParametersConverter jobParametersConverter(DataSource dataSource) {
return new JsrJobParametersConverter(dataSource);
}
#Bean
JobOperator jsrJobOperator(ApplicationContext applicationContext, JobExplorer jobExplorer,
JobRepository jobRepository, JobParametersConverter jobParametersConverter,
PlatformTransactionManager transactionManager) {
JsrJobOperator jobOperator = new JsrJobOperator(jobExplorer, jobRepository, jobParametersConverter,
transactionManager);
jobOperator.setApplicationContext(applicationContext);
jobOperator.setTaskExecutor(new SimpleAsyncTaskExecutor());
return jobOperator;
}
}
https://gist.github.com/rixwwd/8091a717ca24fd810ff71b4fdebbf9cc

Server Sent Event with SQL Database connection using Spring Boot

I want to implement Server Sent Events in Spring Boot. The data is there in SQL database, It is resulting in blocking connection. Web Flux is an option but it is supported only for some NoSQL database.
Yes, you right WebFlux framework doesn't SQL databases in the non blocking mode because reactive drivers does not exist.
But WebFlux provides some instruments to avoid blocking our main threads while we are making blocking long queries to a database.
1) Create configuration with Scheduler where count of threads equals to pool size:
#Configuration
public class SchedulerConfiguration {
#Value("${spring.datasource.maximum-pool-size}
private final Integer connectionPoolSize;
#Bean
#Qualifier("jdbcScheduler")
public Scheduler jdbcScheduler() {
return Schedulers.fromExecutor(Executors.newFixedThreadPool(connectionPoolSize));
}
}
2) Inject your "jdbcScheduler" to the service class:
#Service
public class DataService {
#Autowired
private final DataRepository jdbcRepository;
#Autowired #Qualifier("jdbcScheduler")
private final Scheduler scheduler;
public Mono<String> findById(long id) {
return async(() -> repository.findById(id));
}
private <T> Mono<T> async(Callable<T> callable) {
return Mono.fromCallable(callable).publishOn(scheduler);
}
}
Wrap your blocking method by Mono.fromCallable and delegate from main thread to your "scheduler" via Mono.publishOn
More about schedulers you can read here: Threading and Schedulers
Yes you can achieve asynchronous processing in spring without flux by using their inbuilt #Async processing, here how you can achieve it.
Step1: Enable Aysnc and define a bean for Executor. You can define separate configuration or directly under Main application class.
#SpringBootApplication
#EnableAsync
public class Application {
public static void main(String[] args) {
// close the application context to shut down the custom ExecutorService
SpringApplication.run(Application.class, args).close();
}
#Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("GithubLookup-");
executor.initialize();
return executor;
}
STEP2:
Following is the simple way to configure a method with void return type to run asynchronously, You can also invoke method by retrieve the result of the asynchronous process using the Future object.
#Async
public void asyncMethodWithVoidReturnType() {
System.out.println("Execute method asynchronously. "
+ Thread.currentThread().getName());
}
For more information, You can visit Spring official guide Spring Async

camunda - deployment fluent BPMN model API in spring boot

I would like to ask about camunda process with fluent BPMN model API in spring boot.
What is best practice for setting auto deployment and redeploy (versioning)?
#Configuration
public class BpmProcess {
private final Log logger = LogFactory.getLog(getClass());
#Autowired
private ProcessEngine processEngine;
#Bean
public BpmnModelInstance bpmnModelInstance() {
BpmnModelInstance modelInstance = Bpmn.createExecutableProcess("esign-store")
.name("esign store document")
.startEvent("esign-startEvent")
.serviceTask("esign-uploadStorage")
.name("upload to storage")
.camundaAsyncBefore()
.camundaDelegateExpression("${uploadStorageDelegate}")
.serviceTask("esign-uploadCezar")
.name("upload to cezar")
.camundaAsyncBefore()
.camundaDelegateExpression("${uploadCezarDelegate}")
.endEvent("esign-endEvent")
.camundaAsyncBefore()
.messageEventDefinition("esign-endEventDefinition")
.done();
MessageEventDefinition event = modelInstance.getModelElementById("esign-endEventDefinition");
event.setCamundaDelegateExpression("${endReplyDelegate}");
Bpmn.writeModelToStream(System.out, modelInstance);
return modelInstance;
}
#PostConstruct
public void deploy(){
processEngine.getRepositoryService()
.createDeployment()
.addModelInstance("esign-store.bpmn", bpmnModelInstance())
.name("esign-store-service")
.deploy();
}
}
I don't know if it is correctly.
#PostConstruct is too early, you have to wait until the engine is set up and running. With camunda spring boot, you can listen to the PostDeployEvent and use this a s a hook:
#SpringBootApplication
#EnableProcessApplication
public class MyApp {
...
#EventListener
public void onStart(PostDeployEvent event) {
// deploy here
}
}

SpringBootTest: how to know when boot application is done

Spring boot integration test looks like this
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application)
class IntegrationTest {
static QpidRunner qpidRunner
#BeforeClass
static void init() {
qpidRunner = new QpidRunner()
qpidRunner.start()
}
#AfterClass
static void tearDown() {
qpidRunner.stop()
}
}
So, Qpid instance is run before and teared down after all tests. I want to know is there a way to check whether spring boot application is still running before calling qpidRunner.stop(). I want to stop Qpid only when I'm sure that spring app has finished its stopping.
The Spring Boot integration test can configure an ApplicationListener which listens for ContextClosedEvent. Define a nested #TestConfiguration class inside the test class to add beans to the application's primary configuration.
#TestConfiguration
static class MyConfiguration {
#Bean
public ApplicationListener<ContextClosedEvent> contextClosedEventListener() {
return event -> qpidRunner.stop();
}
}
Taking into account that ConfigurableWebApplicationContext can be injected in a SpringBootTest, adding this lines to the code solved the problem
static ConfigurableWebApplicationContext context
#Autowired
void setContext(ConfigurableWebApplicationContext context) {
AbstractDocsIntegrationTest.context = context
}
#AfterClass
static void tearDown() {
context.stop()
qpidRunner.stop()
}
Spring docs about stop method
Stop this component, typically in a synchronous fashion, such that the
component is fully stopped upon return of this method.
JUnit AfterClass annotated method must be static, therefore #Autowired workaround with setContext method.

Resources