Feat: Node aware job scheduler (#1886)
This commit is contained in:
@@ -45,6 +45,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- DRIVERCLASS_NAME=org.drizzle.jdbc.DrizzleDriver
|
- DRIVERCLASS_NAME=org.drizzle.jdbc.DrizzleDriver
|
||||||
- PROTOCOL=jdbc
|
- PROTOCOL=jdbc
|
||||||
|
- node_id=1
|
||||||
- SUB_PROTOCOL=mysql:thin
|
- SUB_PROTOCOL=mysql:thin
|
||||||
- fineract_tenants_driver=org.drizzle.jdbc.DrizzleDriver
|
- fineract_tenants_driver=org.drizzle.jdbc.DrizzleDriver
|
||||||
- fineract_tenants_url=jdbc:mysql:thin://fineractmysql:3306/fineract_tenants
|
- fineract_tenants_url=jdbc:mysql:thin://fineractmysql:3306/fineract_tenants
|
||||||
|
|||||||
+2
-1
@@ -23,7 +23,8 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
public interface GLAccountRepository extends JpaRepository<GLAccount, Long>, JpaSpecificationExecutor<GLAccount> {
|
public interface GLAccountRepository extends JpaRepository<GLAccount, Long>, JpaSpecificationExecutor<GLAccount> {
|
||||||
|
|
||||||
// no added behaviour
|
// no added behaviour
|
||||||
//adding behaviour to fetch id by glcode for opening balance bulk import
|
// adding behaviour to fetch id by glcode for opening balance bulk import
|
||||||
Optional<GLAccount> findOneByGlCode(String glCode);
|
Optional<GLAccount> findOneByGlCode(String glCode);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -40,7 +40,8 @@ public class GLAccountRepositoryWrapper {
|
|||||||
public GLAccount findOneWithNotFoundDetection(final Long id) {
|
public GLAccount findOneWithNotFoundDetection(final Long id) {
|
||||||
return this.repository.findById(id).orElseThrow(() -> new GLAccountNotFoundException(id));
|
return this.repository.findById(id).orElseThrow(() -> new GLAccountNotFoundException(id));
|
||||||
}
|
}
|
||||||
//finding account id by glcode for opening balance bulk import
|
|
||||||
|
// finding account id by glcode for opening balance bulk import
|
||||||
public GLAccount findOneByGlCodeWithNotFoundDetection(final String glCode) {
|
public GLAccount findOneByGlCodeWithNotFoundDetection(final String glCode) {
|
||||||
return this.repository.findOneByGlCode(glCode).orElseThrow(() -> new GLAccountNotFoundException(glCode));
|
return this.repository.findOneByGlCode(glCode).orElseThrow(() -> new GLAccountNotFoundException(glCode));
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -90,7 +90,8 @@ public class JournalEntryData {
|
|||||||
private String routingCode;
|
private String routingCode;
|
||||||
private String receiptNumber;
|
private String receiptNumber;
|
||||||
private String bankNumber;
|
private String bankNumber;
|
||||||
//for opening bal bulk import
|
|
||||||
|
// for opening bal bulk import
|
||||||
public JournalEntryData(Long officeId, LocalDate transactionDate, String currencyCode, List<CreditDebit> credits,
|
public JournalEntryData(Long officeId, LocalDate transactionDate, String currencyCode, List<CreditDebit> credits,
|
||||||
List<CreditDebit> debits, String locale, String dateFormat) {
|
List<CreditDebit> debits, String locale, String dateFormat) {
|
||||||
this.officeId = officeId;
|
this.officeId = officeId;
|
||||||
|
|||||||
+2
-2
@@ -34,7 +34,7 @@ public final class ChartOfAcountsConstants {
|
|||||||
public static final int TAG_COL = 7;// H
|
public static final int TAG_COL = 7;// H
|
||||||
public static final int TAG_ID_COL = 8;// I
|
public static final int TAG_ID_COL = 8;// I
|
||||||
public static final int DESCRIPTION_COL = 9;// J
|
public static final int DESCRIPTION_COL = 9;// J
|
||||||
//adding for opening balance bulk import
|
// adding for opening balance bulk import
|
||||||
public static final int OFFICE_COL = 10; // K
|
public static final int OFFICE_COL = 10; // K
|
||||||
public static final int OFFICE_COL_ID = 11; // L
|
public static final int OFFICE_COL_ID = 11; // L
|
||||||
public static final int CURRENCY_CODE = 12; // M
|
public static final int CURRENCY_CODE = 12; // M
|
||||||
@@ -47,7 +47,7 @@ public final class ChartOfAcountsConstants {
|
|||||||
public static final int LOOKUP_TAG_COL = 21; // V
|
public static final int LOOKUP_TAG_COL = 21; // V
|
||||||
public static final int LOOKUP_TAG_ID_COL = 22; // W
|
public static final int LOOKUP_TAG_ID_COL = 22; // W
|
||||||
|
|
||||||
//adding for opening balance bulk import
|
// adding for opening balance bulk import
|
||||||
public static final int LOOKUP_OFFICE_COL = 23; // X
|
public static final int LOOKUP_OFFICE_COL = 23; // X
|
||||||
public static final int LOOKUP_OFFICE_ID_COL = 24; // Y
|
public static final int LOOKUP_OFFICE_ID_COL = 24; // Y
|
||||||
|
|
||||||
|
|||||||
+10
-7
@@ -113,9 +113,12 @@ public class ChartOfAccountsWorkbook extends AbstractWorkbookPopulator {
|
|||||||
CellRangeAddressList tagRange = new CellRangeAddressList(1, SpreadsheetVersion.EXCEL97.getLastRowIndex(),
|
CellRangeAddressList tagRange = new CellRangeAddressList(1, SpreadsheetVersion.EXCEL97.getLastRowIndex(),
|
||||||
ChartOfAcountsConstants.TAG_COL, ChartOfAcountsConstants.TAG_COL);
|
ChartOfAcountsConstants.TAG_COL, ChartOfAcountsConstants.TAG_COL);
|
||||||
CellRangeAddressList officeNameRange = new CellRangeAddressList(1, SpreadsheetVersion.EXCEL97.getLastRowIndex(),
|
CellRangeAddressList officeNameRange = new CellRangeAddressList(1, SpreadsheetVersion.EXCEL97.getLastRowIndex(),
|
||||||
ChartOfAcountsConstants.OFFICE_COL, ChartOfAcountsConstants.OFFICE_COL); // validation for opening bal office column
|
ChartOfAcountsConstants.OFFICE_COL, ChartOfAcountsConstants.OFFICE_COL); // validation for opening bal
|
||||||
|
// office column
|
||||||
CellRangeAddressList currencyCodeRange = new CellRangeAddressList(1, SpreadsheetVersion.EXCEL97.getLastRowIndex(),
|
CellRangeAddressList currencyCodeRange = new CellRangeAddressList(1, SpreadsheetVersion.EXCEL97.getLastRowIndex(),
|
||||||
ChartOfAcountsConstants.CURRENCY_CODE, ChartOfAcountsConstants.CURRENCY_CODE);// validation for currency code for opening balance
|
ChartOfAcountsConstants.CURRENCY_CODE, ChartOfAcountsConstants.CURRENCY_CODE);// validation for currency
|
||||||
|
// code for opening
|
||||||
|
// balance
|
||||||
|
|
||||||
DataValidationHelper validationHelper = new HSSFDataValidationHelper((HSSFSheet) chartOfAccountsSheet);
|
DataValidationHelper validationHelper = new HSSFDataValidationHelper((HSSFSheet) chartOfAccountsSheet);
|
||||||
setNames(chartOfAccountsSheet, accountTypesNoDuplicatesList, offices);
|
setNames(chartOfAccountsSheet, accountTypesNoDuplicatesList, offices);
|
||||||
@@ -186,7 +189,7 @@ public class ChartOfAccountsWorkbook extends AbstractWorkbookPopulator {
|
|||||||
writeFormula(ChartOfAcountsConstants.TAG_ID_COL, row,
|
writeFormula(ChartOfAcountsConstants.TAG_ID_COL, row,
|
||||||
"IF(ISERROR(VLOOKUP($H" + (rowNo + 1) + ",$V$2:$W$" + (glAccounts.size() + 1) + ",2,FALSE))," + "\"\",(VLOOKUP($H"
|
"IF(ISERROR(VLOOKUP($H" + (rowNo + 1) + ",$V$2:$W$" + (glAccounts.size() + 1) + ",2,FALSE))," + "\"\",(VLOOKUP($H"
|
||||||
+ (rowNo + 1) + ",$V$2:$W$" + (glAccounts.size() + 1) + ",2,FALSE)))");
|
+ (rowNo + 1) + ",$V$2:$W$" + (glAccounts.size() + 1) + ",2,FALSE)))");
|
||||||
//auto populate office id for bulk import of opening balance
|
// auto populate office id for bulk import of opening balance
|
||||||
writeFormula(ChartOfAcountsConstants.OFFICE_COL_ID, row,
|
writeFormula(ChartOfAcountsConstants.OFFICE_COL_ID, row,
|
||||||
"IF(ISERROR(VLOOKUP($K" + (rowNo + 1) + ",$X$2:$Y$" + (offices.size() + 1) + ",2,FALSE)),\"\",(VLOOKUP($K"
|
"IF(ISERROR(VLOOKUP($K" + (rowNo + 1) + ",$X$2:$Y$" + (offices.size() + 1) + ",2,FALSE)),\"\",(VLOOKUP($K"
|
||||||
+ (rowNo + 1) + ",$X$2:$Y$" + (offices.size() + 1) + ",2,FALSE)))");
|
+ (rowNo + 1) + ",$X$2:$Y$" + (offices.size() + 1) + ",2,FALSE)))");
|
||||||
@@ -236,7 +239,7 @@ public class ChartOfAccountsWorkbook extends AbstractWorkbookPopulator {
|
|||||||
accountTypeIndex++;
|
accountTypeIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//opening balance lookup table of offices
|
// opening balance lookup table of offices
|
||||||
startIndex = 1;
|
startIndex = 1;
|
||||||
rowIndex = 1;
|
rowIndex = 1;
|
||||||
for (OfficeData office : offices) {
|
for (OfficeData office : offices) {
|
||||||
@@ -283,7 +286,7 @@ public class ChartOfAccountsWorkbook extends AbstractWorkbookPopulator {
|
|||||||
chartOfAccountsSheet.setColumnWidth(ChartOfAcountsConstants.LOOKUP_ACCOUNT_ID_COL, TemplatePopulateImportConstants.MEDIUM_COL_SIZE);
|
chartOfAccountsSheet.setColumnWidth(ChartOfAcountsConstants.LOOKUP_ACCOUNT_ID_COL, TemplatePopulateImportConstants.MEDIUM_COL_SIZE);
|
||||||
chartOfAccountsSheet.setColumnWidth(ChartOfAcountsConstants.LOOKUP_TAG_COL, TemplatePopulateImportConstants.SMALL_COL_SIZE);
|
chartOfAccountsSheet.setColumnWidth(ChartOfAcountsConstants.LOOKUP_TAG_COL, TemplatePopulateImportConstants.SMALL_COL_SIZE);
|
||||||
chartOfAccountsSheet.setColumnWidth(ChartOfAcountsConstants.LOOKUP_TAG_ID_COL, TemplatePopulateImportConstants.SMALL_COL_SIZE);
|
chartOfAccountsSheet.setColumnWidth(ChartOfAcountsConstants.LOOKUP_TAG_ID_COL, TemplatePopulateImportConstants.SMALL_COL_SIZE);
|
||||||
//adding lookup for opening balance bulk import
|
// adding lookup for opening balance bulk import
|
||||||
chartOfAccountsSheet.setColumnWidth(ChartOfAcountsConstants.LOOKUP_OFFICE_COL, TemplatePopulateImportConstants.MEDIUM_COL_SIZE);
|
chartOfAccountsSheet.setColumnWidth(ChartOfAcountsConstants.LOOKUP_OFFICE_COL, TemplatePopulateImportConstants.MEDIUM_COL_SIZE);
|
||||||
chartOfAccountsSheet.setColumnWidth(ChartOfAcountsConstants.LOOKUP_OFFICE_ID_COL, TemplatePopulateImportConstants.SMALL_COL_SIZE);
|
chartOfAccountsSheet.setColumnWidth(ChartOfAcountsConstants.LOOKUP_OFFICE_ID_COL, TemplatePopulateImportConstants.SMALL_COL_SIZE);
|
||||||
|
|
||||||
@@ -297,7 +300,7 @@ public class ChartOfAccountsWorkbook extends AbstractWorkbookPopulator {
|
|||||||
writeString(ChartOfAcountsConstants.TAG_COL, rowHeader, "Tag");
|
writeString(ChartOfAcountsConstants.TAG_COL, rowHeader, "Tag");
|
||||||
writeString(ChartOfAcountsConstants.TAG_ID_COL, rowHeader, "Tag Id");
|
writeString(ChartOfAcountsConstants.TAG_ID_COL, rowHeader, "Tag Id");
|
||||||
writeString(ChartOfAcountsConstants.DESCRIPTION_COL, rowHeader, "Description *");
|
writeString(ChartOfAcountsConstants.DESCRIPTION_COL, rowHeader, "Description *");
|
||||||
//adding data for opening balance bulk import
|
// adding data for opening balance bulk import
|
||||||
writeString(ChartOfAcountsConstants.OFFICE_COL, rowHeader, "Parent Office for Opening Balance");
|
writeString(ChartOfAcountsConstants.OFFICE_COL, rowHeader, "Parent Office for Opening Balance");
|
||||||
writeString(ChartOfAcountsConstants.OFFICE_COL_ID, rowHeader, "Parent Office Code Opening Balance");
|
writeString(ChartOfAcountsConstants.OFFICE_COL_ID, rowHeader, "Parent Office Code Opening Balance");
|
||||||
writeString(ChartOfAcountsConstants.CURRENCY_CODE, rowHeader, "Currency Code");
|
writeString(ChartOfAcountsConstants.CURRENCY_CODE, rowHeader, "Currency Code");
|
||||||
@@ -309,7 +312,7 @@ public class ChartOfAccountsWorkbook extends AbstractWorkbookPopulator {
|
|||||||
writeString(ChartOfAcountsConstants.LOOKUP_TAG_ID_COL, rowHeader, "Lookup Tag Id");
|
writeString(ChartOfAcountsConstants.LOOKUP_TAG_ID_COL, rowHeader, "Lookup Tag Id");
|
||||||
writeString(ChartOfAcountsConstants.LOOKUP_ACCOUNT_NAME_COL, rowHeader, "Lookup Account name *");
|
writeString(ChartOfAcountsConstants.LOOKUP_ACCOUNT_NAME_COL, rowHeader, "Lookup Account name *");
|
||||||
writeString(ChartOfAcountsConstants.LOOKUP_ACCOUNT_ID_COL, rowHeader, "Lookup Account Id");
|
writeString(ChartOfAcountsConstants.LOOKUP_ACCOUNT_ID_COL, rowHeader, "Lookup Account Id");
|
||||||
//adding lookup for opening balance bulk import
|
// adding lookup for opening balance bulk import
|
||||||
writeString(ChartOfAcountsConstants.LOOKUP_OFFICE_COL, rowHeader, "Lookup Office Name");
|
writeString(ChartOfAcountsConstants.LOOKUP_OFFICE_COL, rowHeader, "Lookup Office Name");
|
||||||
writeString(ChartOfAcountsConstants.LOOKUP_OFFICE_ID_COL, rowHeader, "Lookup Office Id");
|
writeString(ChartOfAcountsConstants.LOOKUP_OFFICE_ID_COL, rowHeader, "Lookup Office Id");
|
||||||
|
|
||||||
|
|||||||
+18
@@ -41,6 +41,12 @@ public class ScheduledJobDetail extends AbstractPersistableCustom {
|
|||||||
@Column(name = "display_name")
|
@Column(name = "display_name")
|
||||||
private String jobDisplayName;
|
private String jobDisplayName;
|
||||||
|
|
||||||
|
@Column(name = "node_id")
|
||||||
|
private Integer nodeId;
|
||||||
|
|
||||||
|
@Column(name = "is_mismatched_job")
|
||||||
|
private boolean isMismatchedJob;
|
||||||
|
|
||||||
@Column(name = "cron_expression")
|
@Column(name = "cron_expression")
|
||||||
private String cronExpression;
|
private String cronExpression;
|
||||||
|
|
||||||
@@ -135,6 +141,14 @@ public class ScheduledJobDetail extends AbstractPersistableCustom {
|
|||||||
this.jobKey = jobKey;
|
this.jobKey = jobKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getIsMismatchedJob() {
|
||||||
|
return this.isMismatchedJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsMismatchedJob(final boolean isMismatchedJob) {
|
||||||
|
this.isMismatchedJob = isMismatchedJob;
|
||||||
|
}
|
||||||
|
|
||||||
public void updateErrorLog(final String errorLog) {
|
public void updateErrorLog(final String errorLog) {
|
||||||
this.errorLog = errorLog;
|
this.errorLog = errorLog;
|
||||||
}
|
}
|
||||||
@@ -147,6 +161,10 @@ public class ScheduledJobDetail extends AbstractPersistableCustom {
|
|||||||
this.currentlyRunning = currentlyRunning;
|
this.currentlyRunning = currentlyRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getNodeId() {
|
||||||
|
return this.nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, Object> update(final JsonCommand command) {
|
public Map<String, Object> update(final JsonCommand command) {
|
||||||
final Map<String, Object> actualChanges = new LinkedHashMap<>(9);
|
final Map<String, Object> actualChanges = new LinkedHashMap<>(9);
|
||||||
|
|
||||||
|
|||||||
+7
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.infrastructure.jobs.domain;
|
package org.apache.fineract.infrastructure.jobs.domain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import javax.persistence.LockModeType;
|
import javax.persistence.LockModeType;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
@@ -38,4 +39,10 @@ public interface ScheduledJobDetailRepository
|
|||||||
@Query("select jobDetail from ScheduledJobDetail jobDetail where jobDetail.jobKey = :jobKey")
|
@Query("select jobDetail from ScheduledJobDetail jobDetail where jobDetail.jobKey = :jobKey")
|
||||||
ScheduledJobDetail findByJobKeyWithLock(@Param("jobKey") String jobKey);
|
ScheduledJobDetail findByJobKeyWithLock(@Param("jobKey") String jobKey);
|
||||||
|
|
||||||
|
@Query("select jobDetail from ScheduledJobDetail jobDetail where jobDetail.isMismatchedJob = :isMismatchedJob")
|
||||||
|
List<ScheduledJobDetail> findAllMismatchedJobs(@Param("isMismatchedJob") boolean isMismatchedJob);
|
||||||
|
|
||||||
|
@Query("select jobDetail from ScheduledJobDetail jobDetail where jobDetail.nodeId = :nodeId or jobDetail.nodeId = 0")
|
||||||
|
List<ScheduledJobDetail> findAllJobs(@Param("nodeId") Integer nodeId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.fineract.infrastructure.jobs.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class JobNodeIdMismatchingException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public JobNodeIdMismatchingException(final String nodeId, final String nodeIdProvided) {
|
||||||
|
super("error.msg.job.cannot.execute.on.node." + nodeIdProvided,
|
||||||
|
"The node id provided `" + nodeIdProvided + "`" + "` does not match with the configured nodeId.", new Object[] { nodeId });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+2
-1
@@ -46,7 +46,8 @@ public enum JobName {
|
|||||||
"Generate AdhocClient Schedule"), UPDATE_EMAIL_OUTBOUND_WITH_CAMPAIGN_MESSAGE(
|
"Generate AdhocClient Schedule"), UPDATE_EMAIL_OUTBOUND_WITH_CAMPAIGN_MESSAGE(
|
||||||
"Update Email Outbound with campaign message"), EXECUTE_EMAIL(
|
"Update Email Outbound with campaign message"), EXECUTE_EMAIL(
|
||||||
"Execute Email"), UPDATE_TRAIL_BALANCE_DETAILS(
|
"Execute Email"), UPDATE_TRAIL_BALANCE_DETAILS(
|
||||||
"Update Trial Balance Details");
|
"Update Trial Balance Details"), EXECUTE_DIRTY_JOBS(
|
||||||
|
"Execute All Dirty Jobs");
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
|||||||
+25
-4
@@ -37,6 +37,7 @@ import org.apache.fineract.infrastructure.jobs.domain.JobParameter;
|
|||||||
import org.apache.fineract.infrastructure.jobs.domain.JobParameterRepository;
|
import org.apache.fineract.infrastructure.jobs.domain.JobParameterRepository;
|
||||||
import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetail;
|
import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetail;
|
||||||
import org.apache.fineract.infrastructure.jobs.domain.SchedulerDetail;
|
import org.apache.fineract.infrastructure.jobs.domain.SchedulerDetail;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.exception.JobNodeIdMismatchingException;
|
||||||
import org.apache.fineract.infrastructure.jobs.exception.JobNotFoundException;
|
import org.apache.fineract.infrastructure.jobs.exception.JobNotFoundException;
|
||||||
import org.apache.fineract.infrastructure.security.service.TenantDetailsService;
|
import org.apache.fineract.infrastructure.security.service.TenantDetailsService;
|
||||||
import org.quartz.JobDataMap;
|
import org.quartz.JobDataMap;
|
||||||
@@ -50,6 +51,7 @@ import org.quartz.TriggerListener;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
import org.springframework.context.event.ContextClosedEvent;
|
import org.springframework.context.event.ContextClosedEvent;
|
||||||
@@ -118,12 +120,15 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
|
|||||||
this.jobParameterRepository = jobParameterRepository;
|
this.jobParameterRepository = jobParameterRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Value("${node_id:1}")
|
||||||
|
private String nodeId;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void loadAllJobs() {
|
public void loadAllJobs() {
|
||||||
final List<FineractPlatformTenant> allTenants = this.tenantDetailsService.findAllTenants();
|
final List<FineractPlatformTenant> allTenants = this.tenantDetailsService.findAllTenants();
|
||||||
for (final FineractPlatformTenant tenant : allTenants) {
|
for (final FineractPlatformTenant tenant : allTenants) {
|
||||||
ThreadLocalContextUtil.setTenant(tenant);
|
ThreadLocalContextUtil.setTenant(tenant);
|
||||||
final List<ScheduledJobDetail> scheduledJobDetails = this.schedularWritePlatformService.retrieveAllJobs();
|
final List<ScheduledJobDetail> scheduledJobDetails = this.schedularWritePlatformService.retrieveAllJobs(nodeId);
|
||||||
for (final ScheduledJobDetail jobDetails : scheduledJobDetails) {
|
for (final ScheduledJobDetail jobDetails : scheduledJobDetails) {
|
||||||
scheduleJob(jobDetails);
|
scheduleJob(jobDetails);
|
||||||
jobDetails.updateTriggerMisfired(false);
|
jobDetails.updateTriggerMisfired(false);
|
||||||
@@ -203,11 +208,12 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
|
|||||||
schedulerDetail.updateSuspendedState(false);
|
schedulerDetail.updateSuspendedState(false);
|
||||||
this.schedularWritePlatformService.updateSchedulerDetail(schedulerDetail);
|
this.schedularWritePlatformService.updateSchedulerDetail(schedulerDetail);
|
||||||
if (schedulerDetail.isExecuteInstructionForMisfiredJobs()) {
|
if (schedulerDetail.isExecuteInstructionForMisfiredJobs()) {
|
||||||
final List<ScheduledJobDetail> scheduledJobDetails = this.schedularWritePlatformService.retrieveAllJobs();
|
final List<ScheduledJobDetail> scheduledJobDetails = this.schedularWritePlatformService.retrieveAllJobs(this.nodeId);
|
||||||
for (final ScheduledJobDetail jobDetail : scheduledJobDetails) {
|
for (final ScheduledJobDetail jobDetail : scheduledJobDetails) {
|
||||||
if (jobDetail.isTriggerMisfired()) {
|
if (jobDetail.isTriggerMisfired()) {
|
||||||
if (jobDetail.isActiveSchedular()) {
|
if (jobDetail.isActiveSchedular()) {
|
||||||
executeJob(jobDetail, SchedulerServiceConstants.TRIGGER_TYPE_CRON);
|
executeJob(jobDetail, SchedulerServiceConstants.TRIGGER_TYPE_CRON);
|
||||||
|
jobDetail.setIsMismatchedJob(false);
|
||||||
}
|
}
|
||||||
final String schedulerName = getSchedulerName(jobDetail);
|
final String schedulerName = getSchedulerName(jobDetail);
|
||||||
final Scheduler scheduler = this.schedulers.get(schedulerName);
|
final Scheduler scheduler = this.schedulers.get(schedulerName);
|
||||||
@@ -236,7 +242,14 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
|
|||||||
@Override
|
@Override
|
||||||
public void rescheduleJob(final Long jobId) {
|
public void rescheduleJob(final Long jobId) {
|
||||||
final ScheduledJobDetail scheduledJobDetail = this.schedularWritePlatformService.findByJobId(jobId);
|
final ScheduledJobDetail scheduledJobDetail = this.schedularWritePlatformService.findByJobId(jobId);
|
||||||
rescheduleJob(scheduledJobDetail);
|
final String nodeIdStored = scheduledJobDetail.getNodeId().toString();
|
||||||
|
if (nodeIdStored.equals(this.nodeId) || nodeIdStored.equals("0")) {
|
||||||
|
rescheduleJob(scheduledJobDetail);
|
||||||
|
} else {
|
||||||
|
scheduledJobDetail.setIsMismatchedJob(true);
|
||||||
|
this.schedularWritePlatformService.saveOrUpdate(scheduledJobDetail);
|
||||||
|
throw new JobNodeIdMismatchingException(nodeIdStored, this.nodeId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -245,7 +258,15 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
|
|||||||
if (scheduledJobDetail == null) {
|
if (scheduledJobDetail == null) {
|
||||||
throw new JobNotFoundException(String.valueOf(jobId));
|
throw new JobNotFoundException(String.valueOf(jobId));
|
||||||
}
|
}
|
||||||
executeJob(scheduledJobDetail, null);
|
final String nodeIdStored = scheduledJobDetail.getNodeId().toString();
|
||||||
|
|
||||||
|
if (nodeIdStored.equals(this.nodeId) || nodeIdStored.equals("0")) {
|
||||||
|
executeJob(scheduledJobDetail, null);
|
||||||
|
} else {
|
||||||
|
scheduledJobDetail.setIsMismatchedJob(true);
|
||||||
|
this.schedularWritePlatformService.saveOrUpdate(scheduledJobDetail);
|
||||||
|
throw new JobNodeIdMismatchingException(nodeIdStored, this.nodeId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ import org.apache.fineract.infrastructure.jobs.domain.SchedulerDetail;
|
|||||||
|
|
||||||
public interface SchedularWritePlatformService {
|
public interface SchedularWritePlatformService {
|
||||||
|
|
||||||
List<ScheduledJobDetail> retrieveAllJobs();
|
List<ScheduledJobDetail> retrieveAllJobs(String nodeId);
|
||||||
|
|
||||||
ScheduledJobDetail findByJobKey(String triggerKey);
|
ScheduledJobDetail findByJobKey(String triggerKey);
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -58,8 +58,8 @@ public class SchedularWritePlatformServiceJpaRepositoryImpl implements Schedular
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ScheduledJobDetail> retrieveAllJobs() {
|
public List<ScheduledJobDetail> retrieveAllJobs(final String nodeId) {
|
||||||
return this.scheduledJobDetailsRepository.findAll();
|
return this.scheduledJobDetailsRepository.findAllJobs(Integer.parseInt(nodeId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+2
-1
@@ -1647,7 +1647,8 @@ public class Loan extends AbstractPersistableCustom {
|
|||||||
this.fixedPrincipalPercentagePerInstallment)) {
|
this.fixedPrincipalPercentagePerInstallment)) {
|
||||||
this.fixedPrincipalPercentagePerInstallment = command
|
this.fixedPrincipalPercentagePerInstallment = command
|
||||||
.bigDecimalValueOfParameterNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName);
|
.bigDecimalValueOfParameterNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName);
|
||||||
actualChanges.put(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, this.fixedPrincipalPercentagePerInstallment);
|
actualChanges.put(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName,
|
||||||
|
this.fixedPrincipalPercentagePerInstallment);
|
||||||
}
|
}
|
||||||
|
|
||||||
return actualChanges;
|
return actualChanges;
|
||||||
|
|||||||
+2
@@ -39,4 +39,6 @@ public interface ScheduledJobRunnerService {
|
|||||||
void postDividends() throws JobExecutionException;
|
void postDividends() throws JobExecutionException;
|
||||||
|
|
||||||
void updateTrialBalanceDetails() throws JobExecutionException;
|
void updateTrialBalanceDetails() throws JobExecutionException;
|
||||||
|
|
||||||
|
void executeMissMatchedJobs() throws JobExecutionException;
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-1
@@ -38,8 +38,11 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
|
|||||||
import org.apache.fineract.infrastructure.core.service.RoutingDataSourceServiceFactory;
|
import org.apache.fineract.infrastructure.core.service.RoutingDataSourceServiceFactory;
|
||||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
|
import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetail;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository;
|
||||||
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
||||||
import org.apache.fineract.infrastructure.jobs.service.JobName;
|
import org.apache.fineract.infrastructure.jobs.service.JobName;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.service.JobRegisterService;
|
||||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||||
import org.apache.fineract.portfolio.savings.DepositAccountUtils;
|
import org.apache.fineract.portfolio.savings.DepositAccountUtils;
|
||||||
import org.apache.fineract.portfolio.savings.data.DepositAccountData;
|
import org.apache.fineract.portfolio.savings.data.DepositAccountData;
|
||||||
@@ -53,6 +56,7 @@ import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountSchedular
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@@ -74,6 +78,11 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService
|
|||||||
private final ShareAccountDividendReadPlatformService shareAccountDividendReadPlatformService;
|
private final ShareAccountDividendReadPlatformService shareAccountDividendReadPlatformService;
|
||||||
private final ShareAccountSchedularService shareAccountSchedularService;
|
private final ShareAccountSchedularService shareAccountSchedularService;
|
||||||
private final TrialBalanceRepositoryWrapper trialBalanceRepositoryWrapper;
|
private final TrialBalanceRepositoryWrapper trialBalanceRepositoryWrapper;
|
||||||
|
private final JobRegisterService jobRegisterService;
|
||||||
|
private final ScheduledJobDetailRepository scheduledJobDetailsRepository;
|
||||||
|
|
||||||
|
@Value("${node_id:1}")
|
||||||
|
private String nodeId;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ScheduledJobRunnerServiceImpl(final RoutingDataSourceServiceFactory dataSourceServiceFactory,
|
public ScheduledJobRunnerServiceImpl(final RoutingDataSourceServiceFactory dataSourceServiceFactory,
|
||||||
@@ -83,7 +92,8 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService
|
|||||||
final DepositAccountWritePlatformService depositAccountWritePlatformService,
|
final DepositAccountWritePlatformService depositAccountWritePlatformService,
|
||||||
final ShareAccountDividendReadPlatformService shareAccountDividendReadPlatformService,
|
final ShareAccountDividendReadPlatformService shareAccountDividendReadPlatformService,
|
||||||
final ShareAccountSchedularService shareAccountSchedularService,
|
final ShareAccountSchedularService shareAccountSchedularService,
|
||||||
final TrialBalanceRepositoryWrapper trialBalanceRepositoryWrapper) {
|
final TrialBalanceRepositoryWrapper trialBalanceRepositoryWrapper, final JobRegisterService jobRegisterService,
|
||||||
|
final ScheduledJobDetailRepository scheduledJobDetailsRepository) {
|
||||||
this.dataSourceServiceFactory = dataSourceServiceFactory;
|
this.dataSourceServiceFactory = dataSourceServiceFactory;
|
||||||
this.savingsAccountWritePlatformService = savingsAccountWritePlatformService;
|
this.savingsAccountWritePlatformService = savingsAccountWritePlatformService;
|
||||||
this.savingsAccountChargeReadPlatformService = savingsAccountChargeReadPlatformService;
|
this.savingsAccountChargeReadPlatformService = savingsAccountChargeReadPlatformService;
|
||||||
@@ -92,6 +102,8 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService
|
|||||||
this.shareAccountDividendReadPlatformService = shareAccountDividendReadPlatformService;
|
this.shareAccountDividendReadPlatformService = shareAccountDividendReadPlatformService;
|
||||||
this.shareAccountSchedularService = shareAccountSchedularService;
|
this.shareAccountSchedularService = shareAccountSchedularService;
|
||||||
this.trialBalanceRepositoryWrapper = trialBalanceRepositoryWrapper;
|
this.trialBalanceRepositoryWrapper = trialBalanceRepositoryWrapper;
|
||||||
|
this.jobRegisterService = jobRegisterService;
|
||||||
|
this.scheduledJobDetailsRepository = scheduledJobDetailsRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -482,4 +494,16 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CronTarget(jobName = JobName.EXECUTE_DIRTY_JOBS)
|
||||||
|
public void executeMissMatchedJobs() throws JobExecutionException {
|
||||||
|
List<ScheduledJobDetail> jobDetails = this.scheduledJobDetailsRepository.findAllMismatchedJobs(true);
|
||||||
|
|
||||||
|
for (ScheduledJobDetail scheduledJobDetail : jobDetails) {
|
||||||
|
if (scheduledJobDetail.getNodeId().toString().equals(this.nodeId)) {
|
||||||
|
jobRegisterService.executeJob(scheduledJobDetail.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
--
|
||||||
|
-- Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
-- or more contributor license agreements. See the NOTICE file
|
||||||
|
-- distributed with this work for additional information
|
||||||
|
-- regarding copyright ownership. The ASF licenses this file
|
||||||
|
-- to you under the Apache License, Version 2.0 (the
|
||||||
|
-- "License"); you may not use this file except in compliance
|
||||||
|
-- with the License. You may obtain a copy of the License at
|
||||||
|
--
|
||||||
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
--
|
||||||
|
-- Unless required by applicable law or agreed to in writing,
|
||||||
|
-- software distributed under the License is distributed on an
|
||||||
|
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
-- KIND, either express or implied. See the License for the
|
||||||
|
-- specific language governing permissions and limitations
|
||||||
|
-- under the License.
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE job ADD COLUMN node_id int;
|
||||||
|
|
||||||
|
UPDATE job SET node_id = 1;
|
||||||
|
|
||||||
|
ALTER TABLE job ADD COLUMN is_mismatched_job TINYINT(1) DEFAULT 1;
|
||||||
|
|
||||||
|
INSERT INTO `job` (`node_id`, `name`, `display_name`, `cron_expression`, `create_time`, `task_priority`, `group_name`, `previous_run_start_time`, `next_run_time`, `job_key`, `initializing_errorlog`, `is_active`, `currently_running`, `updates_allowed`, `scheduler_group`, `is_misfired`,`is_mismatched_job`) VALUES (0,'Execute All Dirty Jobs', 'Execute All Dirty Jobs', '0 1 0 1/1 * ? *', now(), 5, NULL, NULL, NULL, 'Execute All Dirty JobsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0,0);
|
||||||
@@ -87,6 +87,8 @@ spec:
|
|||||||
value: jdbc
|
value: jdbc
|
||||||
- name: SUB_PROTOCOL
|
- name: SUB_PROTOCOL
|
||||||
value: mysql:thin
|
value: mysql:thin
|
||||||
|
- name: node_id
|
||||||
|
value: 1
|
||||||
- name: fineract_tenants_driver
|
- name: fineract_tenants_driver
|
||||||
value: org.drizzle.jdbc.DrizzleDriver
|
value: org.drizzle.jdbc.DrizzleDriver
|
||||||
- name: fineract_tenants_url
|
- name: fineract_tenants_url
|
||||||
|
|||||||
Reference in New Issue
Block a user