spring.main.allow-bean-definition-overriding: true
server:
port: 8080
spring.devtools:
add-properties: false
livereload:
enabled: false
#spring cron expression see https://riptutorial.com/spring/example/21209/cron-expression, use UTC time zone
cron:
credit: "0 0 4 * * 2-6"
commodity: "* * * * * 2-6"
gsm: "0 0 7 * * 2-6"
scheduledThreadPool:
threadNamePrefix: "lci-task-pool-"
threadCount: 10
# Could set lock time to '5S', '3M' or '0'(means no lock)
# Use 'lockAtLeastFor' to avoid concurrent running on cluster(cluster should use same clock)
# Use 'lockAtMostFor' to avoid indefinite lock time in case the machine which obtained the lock died before releasing it.
scheduler.lock:
credit:
lockAtMostFor: "3M"
lockAtLeastFor: "1M"
commodity:
lockAtMostFor: "3M"
lockAtLeastFor: "30s"
gsm:
lockAtMostFor: "3M"
lockAtLeastFor: "1M"
dependencies {
implementation platform(project(':lci-core:lci-core-hive'))
implementation platform(project(':lci-flow:lci-flow-credit'))
implementation platform(project(':lci-flow:lci-flow-gsm'))
implementation platform(project(':lci-modules:lci-modules-oauth2'))
implementation "org.springframework.boot:spring-boot-starter-webflux"
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb-reactive'
implementation "net.javacrumbs.shedlock:shedlock-spring:${shedlockVersion}"
implementation "net.javacrumbs.shedlock:shedlock-provider-mongo:${shedlockVersion}"
// Test dependencies
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'com.squareup.okhttp3:mockwebserver'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.awaitility:awaitility'
}
jar {
enabled = false
}
bootJar {
enabled = true
archiveFileName = "lci-app.jar"
launchScript {
properties "mode": "run"
}
}
project.afterEvaluate {
springBoot {
buildInfo {
properties {
version = citi.buildVersion.get()
}
}
}
}
springBootVersion=2.3.2.RELEASE
shedlockVersion=4.15.1
package com.citi.xip.lci.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration;
import org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration;
/** Bootstrap entrypoint. */
@SpringBootApplication(
exclude = {
MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class,
MongoRepositoriesAutoConfiguration.class,
MongoReactiveAutoConfiguration.class,
MongoReactiveDataAutoConfiguration.class,
MongoReactiveRepositoriesAutoConfiguration.class
})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
package com.citi.xip.lci.app.schedulers;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
@Value("${scheduledThreadPool.threadCount}")
private int threadPoolSize;
@Value("${scheduledThreadPool.threadNamePrefix}")
private String threadNamePrefix;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(threadPoolSize);
threadPoolTaskScheduler.setThreadNamePrefix(threadNamePrefix);
threadPoolTaskScheduler.initialize();
scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
}
package com.citi.xip.lci.app.schedulers;
public class SchedulerConstant {
public static final String LOCK_UNTIL = "lockUntil";
public static final String ID = "_id";
public static final String SHEDLOCK_COLLECTION_NAME = "shedLock";
public static final String LOCK_NAME_SUFFIX = "SchedulerLock";
public static final String GSM_LOCK_NAME_PREFIX = "gsm";
public static final String CREDIT_LOCK_NAME_PREFIX = "credit";
public static final String COMMODITY_LOCK_NAME_PREFIX = "commodity";
}
package com.citi.xip.lci.app.schedulers;
import static com.citi.xip.lci.app.schedulers.SchedulerConstant.SHEDLOCK_COLLECTION_NAME;
import com.citi.xip.lci.core.base.spring.qualifiers.Quattro;
import com.mongodb.client.MongoCollection;
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.mongo.MongoLockProvider;
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoTemplate;
@Configuration
@EnableSchedulerLock(defaultLockAtMostFor = "0")
public class ShedlockConfig {
@Autowired @Quattro private MongoTemplate template;
@Bean
public LockProvider lockProvider() {
MongoCollection<Document> mongo = template.getCollection(SHEDLOCK_COLLECTION_NAME);
return new MongoLockProvider(mongo);
}
}
package com.citi.xip.lci.app.schedulers;
import static com.citi.xip.lci.app.schedulers.SchedulerConstant.COMMODITY_LOCK_NAME_PREFIX;
import static com.citi.xip.lci.app.schedulers.SchedulerConstant.LOCK_NAME_SUFFIX;
import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class CommodityScheduler {
@Scheduled(cron = "${cron.commodity}", zone = "UTC")
@SchedulerLock(
name = COMMODITY_LOCK_NAME_PREFIX + LOCK_NAME_SUFFIX,
lockAtMostFor = "${scheduler.lock.commodity.lockAtMostFor}",
lockAtLeastFor = "${scheduler.lock.commodity.lockAtLeastFor}")
public void work() {
log.info("Commodity detecting job start!");
}
}
package com.citi.xip.lci.app.controller;
import static com.citi.xip.lci.app.schedulers.SchedulerConstant.ID;
import static com.citi.xip.lci.app.schedulers.SchedulerConstant.LOCK_NAME_SUFFIX;
import static com.citi.xip.lci.app.schedulers.SchedulerConstant.LOCK_UNTIL;
import static com.citi.xip.lci.app.schedulers.SchedulerConstant.SHEDLOCK_COLLECTION_NAME;
import static com.mongodb.client.model.Filters.eq;
import static com.mongodb.client.model.Updates.combine;
import static com.mongodb.client.model.Updates.set;
import com.citi.xip.lci.core.base.spring.qualifiers.Quattro;
import com.mongodb.client.MongoCollection;
import java.time.Instant;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor;
import org.springframework.scheduling.config.ScheduledTask;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
public class SchedulerController {
@Autowired org.springframework.context.ApplicationContext applicationContext;
@Autowired @Quattro private MongoTemplate template;
@GetMapping(path = "/scheduler/executeNow")
public String execute(@RequestParam(value = "business") String business) {
ScheduledAnnotationBeanPostProcessor bean =
applicationContext.getBean(ScheduledAnnotationBeanPostProcessor.class);
for (ScheduledTask task : bean.getScheduledTasks()) {
if (task.toString().toLowerCase().contains(business.toLowerCase())) {
removeLock(business);
task.getTask().getRunnable().run();
return "executed";
}
}
return "No this business task";
}
// If lockUtil > now will not execute and skip
private void removeLock(String business) {
MongoCollection<Document> mongo = template.getCollection(SHEDLOCK_COLLECTION_NAME);
mongo.findOneAndUpdate(
eq(ID, business.toLowerCase() + LOCK_NAME_SUFFIX), combine(set(LOCK_UNTIL, Instant.now())));
}
}