NullPointerException BatchEntity

Hi,

I’m getting the following error:

2021-01-25 14:07:34.064 ERROR 1191884 --- [     accounts-1] ntOperationsAsyncTransactionServiceBasic : ERROR ID: 90fd428c-326b-4723-9283-35eedbf9f5c8 | EXCEPTION: CompletionException | STACKTRACE: [java.util.concurrent.CompletionException: java.lang.NullPointerException

at org.springframework.aop.interceptor.AsyncExecutionAspectSupport.lambda$doSubmit$0(AsyncExecutionAspectSupport.java:273)

at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1604)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)

at java.lang.Thread.run(Thread.java:748)

Caused by: java.lang.NullPointerException

at org.camunda.bpm.engine.impl.batch.BatchEntity.createBatchJobDefinition(BatchEntity.java:279)

at org.camunda.bpm.extension.batch.CustomBatchBuilder.lambda$create$0(CustomBatchBuilder.java:142)

at org.camunda.bpm.engine.impl.interceptor.CommandExecutorImpl.execute(CommandExecutorImpl.java:28)

at org.camunda.bpm.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:110)

at org.camunda.bpm.engine.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:46)

at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)

at org.camunda.bpm.engine.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:44)

at org.camunda.bpm.engine.impl.interceptor.ProcessApplicationContextInterceptor.execute(ProcessApplicationContextInterceptor.java:70)

at org.camunda.bpm.engine.impl.interceptor.CommandCounterInterceptor.execute(CommandCounterInterceptor.java:35)

at org.camunda.bpm.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:33)

at org.camunda.bpm.extension.batch.CustomBatchBuilder.create(CustomBatchBuilder.java:133)

at org.camunda.bpm.extension.batch.CustomBatchBuilder.create(CustomBatchBuilder.java:155)

at com.opessoftware.fatca.backend.service.CalculationAsyncServiceBasic.calculateAccountHoldersList(CalculationAsyncServiceBasic.java:381)

at com.opessoftware.fatca.backend.service.CalculationAsyncServiceBasic$$FastClassBySpringCGLIB$$e76bca01.invoke(<generated>)

at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)

at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)

at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)

at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)

at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)

at org.springframework.aop.interceptor.AsyncExecutionAspectSupport.lambda$doSubmit$0(AsyncExecutionAspectSupport.java:270)

... 4 more​

For some reason the BatchEntity can not get the batchJobHandler, I have project which have the following configuration:

@Bean(name = "fatca-camunda-ds")
public HikariDataSource dataSource() {

HikariConfig hikariConfig = new HikariConfig();

hikariConfig.setJdbcUrl(dbURL);
hikariConfig.setUsername(dbUsername);
hikariConfig.setPassword(dbPassword);

hikariConfig.setPoolName("fatca-camunda-pool");
hikariConfig.setMaximumPoolSize(50);
hikariConfig.setMinimumIdle(2);

hikariConfig.setDriverClassName(dbDriver);
hikariConfig.addDataSourceProperty("cachePrepStmts", "true");
hikariConfig.addDataSourceProperty("prepStmtCacheSize", "250");
hikariConfig.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");

HikariDataSource dataSource = new HikariDataSource(hikariConfig);

return dataSource;
}

@Bean(name = "fatcaCamundaTransactionManager")
public PlatformTransactionManager secondaryTransactionManager(
@Qualifier("fatca-camunda-ds") final DataSource dataSource) {

DataSourceTransactionManager tm = new DataSourceTransactionManager(dataSource);

return tm;
}

@Bean(name = "fatcaProcessEngineConfiguration")
public SpringProcessEngineConfiguration processEngineConfiguration(
@Qualifier("fatcaCamundaTransactionManager") final PlatformTransactionManager ptm,
@Qualifier("fatca-camunda-ds") final DataSource dataSource) {

SpringProcessEngineConfiguration springProcessEngineConfiguration = new SpringProcessEngineConfiguration();

springProcessEngineConfiguration.setApplicationContext(applicationContext);
springProcessEngineConfiguration.setDataSource(dataSource);
springProcessEngineConfiguration.setTransactionManager(ptm);

springProcessEngineConfiguration.setDeployChangedOnly(deployChangedOnly); //true
springProcessEngineConfiguration.setJobExecutorActivate(jobExecution);//true
springProcessEngineConfiguration.setMetricsEnabled(metricsEnabled);//false
springProcessEngineConfiguration.setDbMetricsReporterActivate(metricsDbReporterActivate);//false
springProcessEngineConfiguration.setBatchJobsPerSeed(batchJobsPerSeed);//20
springProcessEngineConfiguration.setInvocationsPerBatchJob(invocationsPerBatchJob);//5
springProcessEngineConfiguration.setDatabaseType(databaseType);//postgres
springProcessEngineConfiguration.setDatabaseSchemaUpdate(databaseSchemaUpdate);//true
springProcessEngineConfiguration.setJdbcBatchProcessing(jdbcBatchProcessing);//true
springProcessEngineConfiguration.setJdbcMaxActiveConnections(6000);
springProcessEngineConfiguration.setHistory(historyLevel);//full

Map<Object, Object> beans = new HashMap<>();

beans.put("individualService", individualService);
beans.put("entityService", entityService);
beans.put("controllingPersonService", controllingPersonService);
beans.put("documentService", documentService);
beans.put("reasonAccountHoldersService", reasonAccountHoldersService);

beans.put("isIndividualBalanceOverThresholdPreexistingAccount",
isIndividualBalanceOverThresholdPreexistingAccount);
beans.put("isIndividualLowerValue", isIndividualLowerValue);
beans.put("isPreexistingAccount", isPreexistingAccount);
beans.put("isIndividualBalanceOverThresholdNewAccount", isIndividualBalanceOverThresholdNewAccount);
beans.put("isBalanceOverThresholdEntityPreexistingAccount", isBalanceOverThresholdEntityPreexistingAccount);

beans.put("isControllingPersonBalanceOverThresholdPreexistingAccount",
isControllingPersonBalanceOverThresholdPreexistingAccount);

springProcessEngineConfiguration.setBeans(beans);

return springProcessEngineConfiguration;
}

@Bean(name = "fatcaProcessEngineFactoryBean")
public ProcessEngineFactoryBean processEngineFactoryBean(
@Qualifier("fatcaProcessEngineConfiguration") final SpringProcessEngineConfiguration springProcessEngineConfiguration) {

ProcessEngineFactoryBean p = new ProcessEngineFactoryBean();

p.setProcessEngineConfiguration(springProcessEngineConfiguration);

return p;
}

@Bean
public CalculationBatchJobHandler simpleCustomCalculationBatchJobHandler() {
return new CalculationBatchJobHandler();
}

@Bean
public ProcessEnginePlugin customCalculationBatchJobHandler(CalculationBatchJobHandler calculationBatchJobHandler) {
return new CustomBatchHandlerPlugin(calculationBatchJobHandler);
}​

and this is the jobHandler

public class CalculationBatchJobHandler extends CustomBatchJobHandler<BatchAccountHolder> {

private static final String TYPE = "calculation-batch-handler";
@Autowired
private RuntimeService runtimeService;

@Override
public String getType() {
return TYPE;
}

@Override
public void execute(List<BatchAccountHolder> data, CommandContext commandContext) {

data.forEach(entry -> {
			logger.info("Work on data: {}", entry.getInstitution(););
			runtimeService.startProcessInstanceByKey("loanApproval");
		});
}

}​

in another project I have the following in a class:

@Autowired
private CalculationBatchJobHandler calculationBatchJobHandler;
@Autowired
private ApplicationContext applicationContext;

and in a method the following to call the custom batch:

ProcessEngineImpl pei = applicationContext.getBean("fatcaProcessEngineFactoryBean", ProcessEngineImpl.class);

CustomBatchBuilder.of(batchAccountHolders).configuration(pei.getProcessEngineConfiguration())
.jobHandler(calculationBatchJobHandler).create();​

I already confirm that the batchAccountHolders, pei and calculationBatchHobHandler variables are not null, I don’t know if something wrong happens inside CustomBatchBuilder or I missing some configuration in the batch extension

Thank you in advance for your help.

Juan.

Hi Juan,

hm thats really strange.

What do you mean exactly with “in another project”? The registration of the job handler (where you provide the ProcessEngineBean) and the place where you create the batch job, has to be the same spring context.

And when do you create the Batch? So when you are in Debug mode, is Camunda able to load the job handler from engine configuration? (Line 240 in BatchEntity)

batchJobHandler = Context.getCommandContext().getProcessEngineConfiguration().getBatchHandlers().get(type);

Maybe it’s also a problem that you create the process engine configuration at your own. (Offtopic: Why do you do this?) You could try to just add the job handler there, like this:

if(springProcessEngineConfiguration.getCustomBatchJobHandlers() != null) {
  springProcessEngineConfiguration.getCustomBatchJobHandlers().add(calculationBatchJobHandler);
} else {
  springProcessEngineConfiguration.setCustomBatchJobHandlers(List.of(calculationBatchJobHandler));
}

Cheers,
Patrick

Hi Patrick,

When I said “another project” I mean that had project 1 which have the custom Process Engine Configuration and the custom batch job handler, the “another project” take the project 1 like dependencies and execute the CustomBatchBuilder.

I tried your suggestion and that fixed my trouble, the custom process engine configuration that I do, missing to set the custom job handlers.

Answer offtopic: We do that because we need to get control of the global transaction, I mean besides the Camunda transaction we have transactions with another database, if some transaction fails we can do the global rollback.

Thanks a lot for your help!

Juan.