Spring Batch Job Param always show zsh : no matches found - java

I'm learning for the first time how to use spring batch.
I did my spring conf like this :
#EnableBatchProcessing
#SpringBootApplication
public class BatchChap4Application {
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Bean
public Job job(){
return jobBuilderFactory.get("basicJob").start(step1()).build();
}
#Bean
public Step step1(){
return stepBuilderFactory.get("step1")
.tasklet((stepContribution, chunkContext) -> {
System.out.println("Hello World");
return RepeatStatus.FINISHED;
}).build();
}
public static void main(String[] args) {
SpringApplication.run(BatchChap4Application.class, args);
}
}
when I run jar manually from command line with string value , it works perfectly.
java -jar batch.jar executionDate=2021/02/21
but when I tried to change the param type from string to date, it always show zsh: no matches found: executionDate(date)=2021/02/21
java -jar batch.jar executionDate(date)=2021/02/21
got an unexpected output like this :
zsh: no matches found: executionDate(date)=2021/02/21**strong text**
I tried to search for error. unfortunately no answer. I'm using macOS for the development.
please help.

You need to escape the parenthesis:
java -jar batch.jar executionDate\(date\)=2021/02/21
Or pass job parameters between single quotes:
java -jar batch.jar 'executionDate(date)=2021/02/21'

Related

Spring Cloud Data Flow datasources overrides spring batch app datasource

I'm setting up an instance of Spring Cloud Data Flow. I've run the following commands:
java -jar spring-cloud-dataflow-server-2.9.2.jar \
--spring.cloud.dataflow.features.streams-enabled=false \
--spring.cloud.dataflow.features.schedules-enabled=true \
--spring.datasource.url=jdbc:postgresql://localhost:5432/batch \
--spring.datasource.username=postgres \
--spring.datasource.password=postgres \
--spring.datasource.driver-class-name=org.postgresql.Driver \
--spring.datasource.initialization_mode=always
I've developed a batch job using spring batch to be deployed in this platform. The job uses two data sources: batch for Spring and task Metadata and app_db for my business logic. When I run the app locally, it persists metadata in batch and my business data in app_db, as expected. The problem is when I try to execute de job inside the Spring Cloud Dataflow. The platform overrides my configured business logic database and uses only the batch database, which is supposed to store metadata only.
application.yaml
spring:
batch:
datasource:
url: jdbc:postgresql://localhost:5432/batch
username: postgres
password: postgres
datasource:
url: jdbc:postgresql://localhost:5432/app_db
username: postgres
password: postgres
DatasourceConfiguration
public class DatasourceConfiguration {
#Bean
#ConfigurationProperties("spring.datasource")
#Primary
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
public DataSource dataSource(DataSourceProperties dataSourceProperties) {
return dataSourceProperties.initializeDataSourceBuilder().build();
}
#Bean(name = "batchDataSourceProperties")
#ConfigurationProperties("spring.batch.datasource")
public DataSourceProperties batchDataSourceProperties() {
return new BatchDataSourceProperties();
}
#Bean(name = "batchDataSource")
public DataSource batchDataSource() {
return batchDataSourceProperties.initializeDataSourceBuilder().build();
}
}
#SpringBootApplication
#EnableTask
#EnableBatchProcessing
public class BatchApplication {
#Bean
public TaskConfigurer taskConfigurer(#Qualifier("batchDataSource") DataSource dataSource) {
return new DefaultTaskConfigurer(dataSource);
}
#Bean
public BatchConfigurer batchConfigurer(#Qualifier("batchDataSource") DataSource dataSource) {
return new DefaultBatchConfigurer(dataSource);
}
public static void main(String[] args) {
SpringApplication.run(BatchApplication.class, args);
}
}
Job
#Bean
public Job startJob(JobBuilderFactory jobBuilderFactory, DataSource dataSource) {
try {
System.out.println(dataSource.getConnection().getMetaData().getURL().toString());
} catch (Exception e) {
//TODO: handle exception
}
}
When I look at the data source,jdbc:postgresql://localhost:5432/app_db will be printed when the batch is executed from local and jdbc:postgresql://localhost:5432/batch will be printed when the batch (task) is executed from SCDF.
I want to know how dataflow is overriding application the spring.datasource even though I am not passing any arguments while executing the task. Please suggest a solution to avoid the overriding of datasource.
One solution I am thinking of is creating AppDatasourceConfiguration(app.datasource) use it. But is there a possibility to use spring.datasource without getting overiddien by SCDF.

How to dynamically load Spring controllers

I have a Spring Boot application that hosts a REST API.
Depending on which files get deployed, I want to be able to have it load additional controllers from what is essentially a "plugin" JAR file.
For example, I'd love to be able to do something like this:
java -jar myapp.jar -Dplugins.directory=/opt/myapp/plugins
Is this possible?
Note: these would not be loaded on the fly; once deployed, the set of plugins will remain fixed. I want one application jar that remains the same in every deployment, and the behavior of the application will be determined by the plugins that are deployed alongside it.
it may not 100% Satisfy your demand.
I have two suggestion.
the easy one.
java -jar stackoverflow-1.0-SNAPSHOT.jar --spring.profiles.active=prod
and put different value "#Profile" on your controller.
#RestController
#Profile("prod")
public class URLOneController {
#PostMapping(value = "/url", consumes="application/json", produces="application/json")
public ResponseEntity<HttpStatus> insertClaim(#RequestBody String messageBody) {
return new ResponseEntity<>(HttpStatus.OK);
}
}
second suggestion ,dynamic load beanDefiniton.
#Configuration
#ConditionalOnProperty(name="external.controller.enable",havingValue = "true")
public class ExternalClassDefinitionProcessor implements
BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Class<?> aClass = null;
try {
aClass = contextClassLoader.loadClass("com.jin.learn.demo.UrlOneController");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(aClass);
beanDefinitionBuilder.addPropertyReference("personDao", "personDao");
BeanDefinition personManagerBeanDefinition = beanDefinitionBuilder
.getRawBeanDefinition();
registry.registerBeanDefinition("UrlOneController",
personManagerBeanDefinition);
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory
beanFactory) throws BeansException {
}
}
package your controller into normal jar(not use spring-boot-maven-plugin )
run your app like this command line
java -Dloader.path="lib/,config/,/home/jin/Desktop/abc/target/abc-1.0-SNAPSHOT.jar" -jar stackoverflow-1.0-SNAPSHOT.jar --external.controller.enable=true
the extra contorller in abc-1.0-SNAPSHOT.jar and your main app is stackoverflow-1.0-SNAPSHOT.jar
tips:
stackoverflow-1.0-SNAPSHOT.jar should package zip format .
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>ZIP</layout>
</configuration>
</plugin>

Problem rising spring batch version 3.0.7 to 3.0.9

I am working on a Java project using the Spring Batch framework and I have to upgrade it from version 3.0.7 to 3.0.9 but I have a problem :
#Bean
public Step bonjourRetourJpaToX(StepBuilderFactory stepBuilderFactory, TaskExecutor taskExecutor,
ItemProcessor<BonjourRetourGroup, BonjourElementBlocAgent> compBonjourRetourBonjourProcessor,
#Qualifier("promotionListenerBonjourRetour") ExecutionContextPromotionListener promotionListenerBonjourRetour) {
return stepBuilderFactory.get("bonjourRetourJpaToX").<BonjourRetourGroup, BonjourElementBlocAgent>chunk(batchSizeLoadXml)
.reader(bonjourRetourJpaReader)
.processor(compBonjourRetourBonjourProcessor)
.writer(bonjourRetourXmlWriter)
.taskExecutor(taskExecutor)
.listener(promotionListenerBonjourRetour)
.listener(dsBonjourRetourFinalProcessor())
.listener(dsBonjourRetourTemporaryProcessor)
.listener(bonjourRetourBonjourBoucleStepListener())
.throttleLimit(bonjourRetourJobThrottleLimit)
.build();
}
With the maven compilation error :
[ERROR] src/main/java/source/bonjourRetour/batch/BonjourRetourJobConfig.java:[1025,33] cannot find symbol
symbol: method throttleLimit(int)
This step is multi-threaded
For Mahmoud Ben Hassine :
The composite processor ->
#Scope(value = "step", proxyMode = ScopedProxyMode.NO)
#Bean(name = "compBonjourRetourBonjourProcessorX")
public ItemProcessor<BonjourRetourGroup, XElementBlocAgent> compBonjourRetourBonjourProcessorX(#Value("#{stepExecution}") final StepExecution stepExecution) {
CompositeItemProcessor<BonjourRetourGroup, XElementBlocAgent> compositeProcessor = new CompositeItemProcessor<>();
compositeProcessor
.setDelegates(Arrays.asList(dsBonjourRetourXTemporaryProcessor, dsBonjourRetourXinalProcessor()));
return compositeProcessor;
}
I noticed that when I moved the listener : bonjourRetourBonjourBoucleStepListener(); like that it compiles :
#Bean
public Step bonjourRetourJpaToX(StepBuilderFactory stepBuilderFactory, TaskExecutor
taskExecutor,
ItemProcessor<BonjourRetourGroup, BonjourElementBlocAgent> compBonjourRetourBonjourProcessor,
#Qualifier("promotionListenerBonjourRetour") ExecutionContextPromotionListener promotionListenerBonjourRetour) {
return stepBuilderFactory.get("bonjourRetourJpaToX").<BonjourRetourGroup, BonjourElementBlocAgent>chunk(batchSizeLoadXml)
.reader(bonjourRetourJpaReader)
.processor(compBonjourRetourBonjourProcessor)
.writer(bonjourRetourXmlWriter)
.taskExecutor(taskExecutor)
.listener(promotionListenerBonjourRetour)
.listener(dsBonjourRetourFinalProcessor())
.listener(bonjourRetourBonjourBoucleStepListener())
//SWITCHED
.listener(dsBonjourRetourTemporaryProcessor)
.throttleLimit(bonjourRetourJobThrottleLimit)
.build();
I know that dsBonjourRetourTemporaryProcessor return an ItemProcessor and bonjourRetourBonjourBoucleStepListener return a StepExecutionListener
For the other parts of my project with this problem, when I moved a StepExecutionListener, it works
Maybe the problem is here ?

Executing a single command and exiting from Spring Shell 2

I stumbled upon this question from a while back, explaining how to get a Spring Shell application to exit after calling it from the command line with a single command. However, testing this in 2.0.0 with Spring Boot, it does not seem to be the case any more that invoking the JAR with command arguments will execute that command and then exit. The shell just starts as normal without executing the supplied command. Is it still possible to do this? If not, would it be possible to pass the arguments from the JAR execution to Spring Shell and then trigger an exit after execution?
For example, let's say I have a command, import that has a couple options. It could be run in the shell like this:
$ java -jar my-app.jar
> import -f /path/to/file.txt --overwrite
Successfully imported 'file.txt'
> exit
But it would be nice to be able to simply execute and exit, for the sake of building a script that can utilize this function:
$ java -jar my-app.jar import -f /path/to/file.txt --overwrite
Successfully imported 'file.txt'
Run it with #my-script, like so:
java -jar my-app.jar #my-script
Where my-script is a file with your commands:
import -f /path/to/file.txt --overwrite
Just to add, I found another way of doing this, doesn't give you the option to run in interactive mode but using your profiles above you can of course swap the configuration. Please note I'm using lombok and jool (just in case anyone copy pastes and gets funny issues!)
Entry
#SpringBootApplication
public class Righter {
public static void main(String[] args) {
SpringApplication.run(Righter.class, args);
}
#Bean
public ApplicationRunner shellRunner(Shell shell) {
return new NonInteractiveShellRunner(shell);
}
Application runner:
#Order(0)
public class NonInteractiveShellRunner implements ApplicationRunner{
private final Shell shell;
public NonInteractiveShellRunner(Shell shell) {
this.shell = shell;
}
#Override
public void run(ApplicationArguments args) throws Exception {
shell.run(new CommandInputProvider(args.getSourceArgs()));
}
public static class PredefinedInputProvider implements InputProvider{
private final Input input;
private boolean commandExecuted = false;
public PredefinedInputProvider(String[] args) {
this.input = new PredefinedInput(args);
}
#Override
public Input readInput() {
if (!commandExecuted){
commandExecuted=true;
return input;
}
return new PredefinedInput(new String[]{"exit"});
}
#AllArgsConstructor
private static class PredefinedInput implements Input{
private final String[] args;
#Override
public String rawText() {
return Seq.of(args).toString(" ");
}
#Override
public List<String> words(){
return Arrays.asList(args);
}
}
}
}
A way to add a single command run mode without excluding interactive mode and script mode (Tested on spring-shell-starter::2.0.0.RELEASE).
Create a runner by analogy with ScriptShellApplicationRunner.
// Runs before ScriptShellApplicationRunner and InteractiveShellApplicationRunner
#Order(InteractiveShellApplicationRunner.PRECEDENCE - 200)
public class SingleCommandApplicationRunner implements ApplicationRunner {
private final Parser parser;
private final Shell shell;
private final ConfigurableEnvironment environment;
private final Set<String> allCommandNames;
public SingleCommandApplicationRunner(
Parser parser,
Shell shell,
ConfigurableEnvironment environment,
Set<CustomCommand> customCommands
) {
this.parser = parser;
this.shell = shell;
this.environment = environment;
this.allCommandNames = buildAllCommandNames(customCommands);
}
private Set<String> buildAllCommandNames(Collection<CustomCommand> customCommands) {
final Set<String> result = new HashSet<>();
customCommands.stream().map(CustomCommand::keys).flatMap(Collection::stream).forEach(result::add);
// default spring shell commands
result.addAll(asList("clear", "exit", "quit", "help", "script", "stacktrace"));
return result;
}
#Override
public void run(ApplicationArguments args) throws Exception {
final boolean singleCommand = haveCommand(args.getSourceArgs());
if (singleCommand) {
InteractiveShellApplicationRunner.disable(environment);
final String fullArgs = join(" ", args.getSourceArgs());
try (Reader reader = new StringReader(fullArgs);
FileInputProvider inputProvider = new FileInputProvider(reader, parser)) {
shell.run(inputProvider);
}
}
}
private boolean haveCommand(String... args) {
for (String arg : args) {
if (allCommandNames.contains(arg)) {
return true;
}
}
return false;
}
}
Register the runner as bean.
#Configuration
class ContextConfiguration {
#Autowired
private Shell shell;
#Bean
SingleCommandApplicationRunner singleCommandApplicationRunner(
Parser parser,
ConfigurableEnvironment environment,
Set<CustomCommand> customCommands
) {
return new SingleCommandApplicationRunner(parser, shell, environment, customCommands);
}
}
So that the runner starts only when the command is sent, we create an interface.
public interface CustomCommand {
Collection<String> keys();
}
Implement CustomCommand interface in each commands.
#ShellComponent
#RequiredArgsConstructor
class MyCommand implements CustomCommand {
private static final String KEY = "my-command";
#Override
public Collection<String> keys() {
return singletonList(KEY);
}
#ShellMethod(key = KEY, value = "My custom command.")
public AttributedString version() {
return "Hello, single command mode!";
}
}
Done!
Run in interactive mode:
java -jar myApp.jar
// 2021-01-14 19:28:16.911 INFO 67313 --- [main] com.nao4j.example.Application: Starting Application v1.0.0 using Java 1.8.0_275 on Apple-MacBook-Pro-15.local with PID 67313 (/Users/nao4j/example/target/myApp.jar started by nao4j in /Users/nao4j/example/target)
// 2021-01-14 19:28:16.916 INFO 67313 --- [main] com.nao4j.example.Application: No active profile set, falling back to default profiles: default
// 2021-01-14 19:28:18.227 INFO 67313 --- [main] com.nao4j.example.Application: Started Application in 2.179 seconds (JVM running for 2.796)
// shell:>my-command
// Hello, single command mode!
Run script from file script.txt (contains text "my-command"):
java -jar myApp.jar #script.txt
// 2021-01-14 19:28:16.911 INFO 67313 --- [main] com.nao4j.example.Application: Starting Application v1.0.0 using Java 1.8.0_275 on Apple-MacBook-Pro-15.local with PID 67313 (/Users/nao4j/example/target/myApp.jar started by nao4j in /Users/nao4j/example/target)
// 2021-01-14 19:28:16.916 INFO 67313 --- [main] com.nao4j.example.Application: No active profile set, falling back to default profiles: default
// 2021-01-14 19:28:18.227 INFO 67313 --- [main] com.nao4j.example.Application: Started Application in 2.179 seconds (JVM running for 2.796)
// Hello, single command mode!
Run in single command mode:
java -jar myApp.jar my-command
// 2021-01-14 19:28:16.911 INFO 67313 --- [main] com.nao4j.example.Application: Starting Application v1.0.0 using Java 1.8.0_275 on Apple-MacBook-Pro-15.local with PID 67313 (/Users/nao4j/example/target/myApp.jar started by nao4j in /Users/nao4j/example/target)
// 2021-01-14 19:28:16.916 INFO 67313 --- [main] com.nao4j.example.Application: No active profile set, falling back to default profiles: default
// 2021-01-14 19:28:18.227 INFO 67313 --- [main] com.nao4j.example.Application: Started Application in 2.179 seconds (JVM running for 2.796)
// Hello, single command mode!
In addition to Alex answers, here is the simpler version of NonInteractiveApplicationRunner I made.
#Component
#Order(InteractiveShellApplicationRunner.PRECEDENCE - 100)
class NonInteractiveApplicationRunner implements ApplicationRunner {
private final Shell shell;
private final ConfigurableEnvironment environment;
public NonInteractiveApplicationRunner(Shell shell, ConfigurableEnvironment environment) {
this.shell = shell;
this.environment = environment;
}
#Override
public void run(ApplicationArguments args) {
if (args.getSourceArgs().length > 0) {
InteractiveShellApplicationRunner.disable(environment);
var input = String.join(" ", args.getSourceArgs());
shell.evaluate(() -> input);
shell.evaluate(() -> "exit");
}
}
}
Using #Component, we don't need to add bean method. In addition, using shell.evaluate() method looks much simpler compare to shell.run(...).
I found a nice little work-around. Rather than creating an ApplicationRunner that mimics the v1 behavior (which is tricky, since JLineInputProvider is a private class), I created one that is optionally loaded, based on active Spring profile. I used JCommander to define the CLI parameters, allowing me to have identical commands for the interactive shell and the one-off executions. Running the Spring Boot JAR with no args triggers the interactive shell. Running it with arguments triggers the one-and-done execution.
#Parameters
public class ImportParameters {
#Parameter(names = { "-f", "--file" }, required = true, description = "Data file")
private File file;
#Parameter(names = { "-t", "--type" }, required = true, description = "Data type")
private DataType dataType;
#Parameter(names = { "-o", "--overwrite" }, description = "Flag to overwrite file if it exists")
private Boolean overwrite = false;
/* getters and setters */
}
public class ImportCommandExecutor {
public void run(ImportParameters params) throws Exception {
// import logic goes here
}
}
/* Handles interactive shell command execution */
#ShellComponent
public class JLineInputExecutor {
// All command executors are injected here
#Autowired private ImportCommandExecutor importExecutor;
...
#ShellMethod(key = "import", value = "Imports the a file of a specified type.")
public String importCommand(#ShellOption(optOut = true) ImportParameters params) throws Exception {
importCommandExecutor.run(params);
}
...
}
/* Handles one-off command execution */
public class JCommanderInputExecutor implements ApplicationRunner {
// All command executors are injected here
#Autowired private ImportCommandExecutor importExecutor;
...
#Override
public void run(ApplicationArguments args) throws Exception {
// Create all of the JCommander argument handler objects
BaseParameters baseParameters = new BaseParameters();
ImportParameters importParameters = new ImportParameters();
...
JCommander jc = newBuilder().
.acceptUnknownOptions(true)
.addObject(baseParameters)
.addCommand("import", importParameters)
...
.build();
jc.parse(args);
String mainCommand = jc.getParsedCommand();
if ("import".equals(mainCommand)){
importExecutor.run(importParameters);
} else if (...) {
...
}
}
}
#Configuration
#Profile({"CLI"})
public class CommandLineInterfaceConfiguration {
// All of my command executors are defined as beans here, as well as other required configurations for both modes of execution
#Bean
public ImportCommandExecutor importExecutor (){
return new ImportCommandExecutor();
}
...
}
#Configuration
#Profile({"SINGLE_COMMAND"})
public class SingleCommandConfiguration {
#Bean
public JCommanderInputExecutor commandLineInputExecutor(){
return new JCommanderInputExecutor();
}
}
#SpringBootApplication
public class Application {
public static void main(String[] args) throws IOException {
String[] profiles = getActiveProfiles(args);
SpringApplicationBuilder builder = new SpringApplicationBuilder(Application.class);
builder.bannerMode((Mode.LOG));
builder.web(false);
builder.profiles(profiles);
System.out.println(String.format("Command line arguments: %s Profiles: %s",
Arrays.asList(args), Arrays.asList(profiles)));
builder.run(args);
}
private static String[] getActiveProfiles(String[] args){
return Arrays.asList(args).contains("-X") ? new String[]{"CLI", "SINGLE_COMMAND"} : new String[]{"CLI"};
}
}
So now I can trigger the interactive client by simply running my executable JAR:
java -jar app.jar
> import -f /path/to/file.txt -t GENE -o
> quit()
Or, if I pass the '-X' argument on the command line, the application will execute and then exit:
java -jar app.jar -X import -f /path/to/file.txt -t GENE -o
In linux works this way too:
echo "import -f /path/to/file.txt --overwrite" | java -jar my-app.jar
despite the fact that it ends with a failure; it happens only after the command is successfully executed.

Scheduling jobs using dropwizard-sundial

I am trying to use dropwizard-sundial and am having trouble with a resource. I'm not sure if it's a classpath issue or if I am failing to register resources properly.
This is my application class' run method:
public void run(DataLoaderApplicationConfiguration configuration, Environment environment) throws Exception {
logger.info("Started DataLoader Application");
final String template = configuration.getTemplate();
environment.healthChecks().register("TemplateHealth", new TemplateHealthCheck(template));
// JOBS
environment.jersey().packages("com.tradier.dataloader.jobs");
}
I get the following error at runtime:
INFO [2015-04-07 15:00:19,737] com.xeiam.sundial.plugins.AnnotationJobTriggerPlugin: Loading annotated jobs from com.tradier.dataloader.jobs.
[WARNING]
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:293)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.RuntimeException: Unexpected problem: No resource for com/tradier/dataloader/jobs
at org.quartz.classloading.CascadingClassLoadHelper.getJobClasses(CascadingClassLoadHelper.java:217)
at com.xeiam.sundial.plugins.AnnotationJobTriggerPlugin.start(AnnotationJobTriggerPlugin.java:72)
at org.quartz.QuartzScheduler.startPlugins(QuartzScheduler.java:1102)
at org.quartz.QuartzScheduler.start(QuartzScheduler.java:211)
at com.xeiam.sundial.SundialJobScheduler.startScheduler(SundialJobScheduler.java:102)
Check out a working example at https://github.com/timmolter/XDropWizard. It uses annotated jobs. You need to add the package name conatining the annotated jobs in your config.yaml file like this:
sundial:
thread-pool-size: 5
shutdown-on-unload: true
wait-on-shutdown: false
start-delay-seconds: 0
start-scheduler-on-load: true
global-lock-on-load: false
annotated-jobs-package-name: org.knowm.xdropwizard.jobs
If you still are getting an exception, leave a report at: https://github.com/timmolter/dropwizard-sundial/issues.
#Jeyashree Narayanan, the jobs package should not be configured in the application class as you have shown, it can be easily done in the yml file. Here is the explanation in simple steps:
Step 1: Configuration in yml file and the Configuration class
sundial:
thread-pool-size: 10
shutdown-on-unload: true
start-delay-seconds: 0
start-scheduler-on-load: true
global-lock-on-load: false
annotated-jobs-package-name: com.tradier.dataloader.jobs
tasks: [startjob, stopjob]
Configuration Class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class DropwizardSundialConfiguration extends Configuration {
#Valid
#NotNull
public SundialConfiguration sundialConfiguration = new SundialConfiguration();
#JsonProperty("sundial")
public SundialConfiguration getSundialConfiguration() {
return sundialConfiguration;
}
}
Step 2: Add and configure the dropwizard-sundial bundle in the application class.
public class DropwizardSundialApplication extends Application<DropwizardSundialConfiguration> {
private static final Logger logger = LoggerFactory.getLogger(DropwizardSundialApplication.class);
public static void main(String[] args) throws Exception {
new DropwizardSundialApplication().run("server", args[0]);
}
#Override
public void initialize(Bootstrap<DropwizardSundialConfiguration> b) {
b.addBundle(new SundialBundle<DropwizardSundialConfiguration>() {
#Override
public SundialConfiguration getSundialConfiguration(DropwizardSundialConfiguration configuration) {
return configuration.getSundialConfiguration();
}
});
}
}
Step 3: Add the required job classes.
Here is a sample Cron job class:
#CronTrigger(cron = "0 19 13 * * ?")
public class CronJob extends Job {
private static final Logger logger = LoggerFactory.getLogger(CronJob.class);
#Override
public void doRun() throws JobInterruptException {
logger.info("Hello from Cron Job");
}
}
I have also written a blog post and a working application which is available on GitHub with these steps. Please check: http://softwaredevelopercentral.blogspot.com/2019/05/dropwizard-sundial-scheduler-tutorial.html
It appears to be a classpath issue.
From https://github.com/timmolter/Sundial/blob/develop/src/main/java/com/xeiam/sundial/SundialJobScheduler.java#L102:
public static void startScheduler(int threadPoolSize, String annotatedJobsPackageName) {
try {
createScheduler(threadPoolSize, annotatedJobsPackageName);
getScheduler().start(); // ---> Line 102
} catch (SchedulerException e) {
logger.error("COULD NOT START SUNDIAL SCHEDULER!!!", e);
throw new SchedulerStartupException(e);
}
I'm also using Sundial in my dropwizard project, I have all my jobs defined in jobs.xml, Sundial config defined in the .yaml file, and start it as follows:
SundialJobScheduler.startScheduler();
SundialManager sm = new SundialManager(config.getSundialConfiguration(),environment);
environment.lifecycle().manage(sm);

Categories

Resources