FINERACT-1678-Extending-jobs-with-module-system

This commit is contained in:
Ruchi Dhamankar
2022-11-23 15:51:00 +01:00
committed by Arnold Gálovics
parent 4b290663d3
commit c1df5e73a2
17 changed files with 488 additions and 11 deletions
+1
View File
@@ -19,4 +19,5 @@
dependencies {
implementation(project(':fineract-provider'))
implementation('org.springframework.boot:spring-boot-starter-data-jpa')
}
+25
View File
@@ -0,0 +1,25 @@
/**
* 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.
*/
description = 'ACME Corp.: Fineract Loan Job'
group = 'com.acme.fineract'
archivesBaseName = 'acme-fineract-loan-job'
apply from: 'dependencies.gradle'
+24
View File
@@ -0,0 +1,24 @@
/**
* 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.
*/
dependencies {
implementation(project(':fineract-provider'))
implementation('org.springframework.batch:spring-batch-integration')
implementation('org.springframework.boot:spring-boot-starter-data-jpa')
}
@@ -0,0 +1,35 @@
/**
* 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 com.acme.fineract.loan.job;
public enum AcmeJobName {
ACME_NOOP_JOB("Acme Noop Job");
private final String name;
AcmeJobName(final String name) {
this.name = name;
}
@Override
public String toString() {
return this.name;
}
}
@@ -0,0 +1,35 @@
/**
* 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 com.acme.fineract.loan.job;
import java.util.List;
import org.apache.fineract.infrastructure.jobs.service.jobname.JobNameData;
import org.apache.fineract.infrastructure.jobs.service.jobname.JobNameProvider;
import org.apache.fineract.infrastructure.jobs.service.jobname.SimpleJobNameProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AcmeJobNameConfig {
@Bean
public JobNameProvider acmeJobNameProvider() {
return new SimpleJobNameProvider(List.of(new JobNameData(AcmeJobName.ACME_NOOP_JOB.name(), AcmeJobName.ACME_NOOP_JOB.toString())));
}
}
@@ -0,0 +1,50 @@
/**
* 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 com.acme.fineract.loan.job;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AcmeNoopJobConfiguration {
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Autowired
private AcmeNoopJobTasklet tasklet;
@Bean
protected Step acmeNoopJobStep() {
return steps.get(AcmeJobName.ACME_NOOP_JOB.name()).tasklet(tasklet).build();
}
@Bean
public Job acmeNoopJob() {
return jobs.get(AcmeJobName.ACME_NOOP_JOB.name()).start(acmeNoopJobStep()).incrementer(new RunIdIncrementer()).build();
}
}
@@ -0,0 +1,37 @@
/**
* 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 com.acme.fineract.loan.job;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class AcmeNoopJobTasklet implements Tasklet {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("Acme custom job execution");
return RepeatStatus.FINISHED;
}
}
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<changeSet author="acme" id="1">
<insert tableName="job">
<column name="name" value="Acme Noop Job"/>
<column name="display_name" value="Acme Noop Job"/>
<column name="cron_expression" value="0 1 0 1/1 * ? *"/>
<column name="create_time" valueDate="${current_datetime}"/>
<column name="task_priority" valueNumeric="5"/>
<column name="group_name"/>
<column name="previous_run_start_time"/>
<column name="job_key" value="Acme Noop Job _ DEFAULT"/>
<column name="initializing_errorlog"/>
<column name="is_active" valueBoolean="false"/>
<column name="currently_running" valueBoolean="false"/>
<column name="updates_allowed" valueBoolean="true"/>
<column name="scheduler_group" valueNumeric="0"/>
<column name="is_misfired" valueBoolean="false"/>
<column name="node_id" valueNumeric="1"/>
<column name="is_mismatched_job" valueBoolean="true"/>
</insert>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,53 @@
/**
* 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.
*/
import static org.junit.Assert.assertEquals;
import com.acme.fineract.loan.job.AcmeNoopJobTasklet;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.repeat.RepeatStatus;
@ExtendWith(MockitoExtension.class)
public class AcmeNoopJobTaskletTest {
@Mock
private StepContribution stepContribution;
@Mock
private ChunkContext chunkContext;
private RepeatStatus resultStatus;
private AcmeNoopJobTasklet underTest;
@BeforeEach
public void setUp() {
underTest = new AcmeNoopJobTasklet();
}
@Test
public void testJobExecution() throws Exception {
resultStatus = underTest.execute(stepContribution, chunkContext);
assertEquals(resultStatus, RepeatStatus.FINISHED);
}
}
@@ -29,7 +29,8 @@ import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
@AutoConfiguration
@ComponentScans({ @ComponentScan("com.acme.fineract.loan.cob"), @ComponentScan("com.acme.fineract.loan.processor") })
@ComponentScans({ @ComponentScan("com.acme.fineract.loan.cob"), @ComponentScan("com.acme.fineract.loan.processor"),
@ComponentScan("com.acme.fineract.loan.job") })
@ConditionalOnProperty("acme.loan.enabled")
public class AcmeLoanAutoConfiguration {
@@ -0,0 +1,38 @@
/**
* 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.config;
import java.util.Arrays;
import java.util.List;
import org.apache.fineract.infrastructure.jobs.service.JobName;
import org.apache.fineract.infrastructure.jobs.service.jobname.JobNameData;
import org.apache.fineract.infrastructure.jobs.service.jobname.JobNameProvider;
import org.apache.fineract.infrastructure.jobs.service.jobname.SimpleJobNameProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JobNameProviderConfig {
@Bean
public JobNameProvider fineractJobNameProvider() {
List<JobNameData> jobNames = Arrays.stream(JobName.values()).map(jn -> new JobNameData(jn.name(), jn.toString())).toList();
return new SimpleJobNameProvider(jobNames);
}
}
@@ -18,9 +18,6 @@
*/
package org.apache.fineract.infrastructure.jobs.service;
import java.util.Arrays;
import java.util.Optional;
public enum JobName {
UPDATE_LOAN_ARREARS_AGEING("Update Loan Arrears Ageing"), //
@@ -66,13 +63,9 @@ public enum JobName {
this.name = name;
}
public static JobName getJobByName(String jobName) {
Optional<JobName> optionalJob = Arrays.stream(JobName.values()).filter(jn -> jobName.equals(jn.name)).findAny();
return optionalJob.orElseThrow(() -> new IllegalArgumentException("Job not found by name: " + jobName));
}
@Override
public String toString() {
return this.name;
}
}
@@ -33,6 +33,8 @@ import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetail;
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.service.jobname.JobNameData;
import org.apache.fineract.infrastructure.jobs.service.jobname.JobNameService;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
@@ -79,6 +81,9 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
@Autowired
private JobStarter jobStarter;
@Autowired
private JobNameService jobNameService;
private static final String JOB_STARTER_METHOD_NAME = "run";
public void executeJob(final ScheduledJobDetail scheduledJobDetail, String triggerType) {
@@ -313,8 +318,8 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
private JobDetail createJobDetail(final ScheduledJobDetail scheduledJobDetail) throws Exception {
final FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
JobName jobName = JobName.getJobByName(scheduledJobDetail.getJobName());
Job job = jobLocator.getJob(jobName.name());
JobNameData jobName = jobNameService.getJobByHumanReadableName(scheduledJobDetail.getJobName());
Job job = jobLocator.getJob(jobName.getEnumStyleName());
final MethodInvokingJobDetailFactoryBean jobDetailFactoryBean = new MethodInvokingJobDetailFactoryBean();
jobDetailFactoryBean.setName(scheduledJobDetail.getJobName() + "JobDetail" + tenant.getId());
@@ -0,0 +1,30 @@
/**
* 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.service.jobname;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public class JobNameData {
private final String enumStyleName;
private final String humanReadableName;
}
@@ -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.
*/
package org.apache.fineract.infrastructure.jobs.service.jobname;
import java.util.Set;
public interface JobNameProvider {
Set<JobNameData> provide();
}
@@ -0,0 +1,43 @@
/**
* 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.service.jobname;
import static java.util.stream.Collectors.toSet;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class JobNameService {
private final Collection<JobNameProvider> providers;
public JobNameData getJobByHumanReadableName(String jobName) {
Optional<JobNameData> optionalJob = getJobNames().stream().filter(jn -> jobName.equals(jn.getHumanReadableName())).findAny();
return optionalJob.orElseThrow(() -> new IllegalArgumentException("Job not found by name: " + jobName));
}
private Set<JobNameData> getJobNames() {
return providers.stream().map(JobNameProvider::provide).flatMap(Collection::stream).collect(toSet());
}
}
@@ -0,0 +1,36 @@
/**
* 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.service.jobname;
import java.util.Collection;
import java.util.Set;
public class SimpleJobNameProvider implements JobNameProvider {
private final Set<JobNameData> jobNames;
public SimpleJobNameProvider(Collection<JobNameData> jobNames) {
this.jobNames = Set.copyOf(jobNames);
}
@Override
public Set<JobNameData> provide() {
return jobNames;
}
}