FINERACT-2194: Batch jobs arre failing in a multitenant environment

This commit is contained in:
Arnold Galovics
2025-02-27 16:25:18 +01:00
committed by Arnold Gálovics
parent 08dc3b123f
commit c1ec3d7fc7
11 changed files with 328 additions and 82 deletions
@@ -0,0 +1,61 @@
/**
* 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;
import java.lang.reflect.Method;
import java.util.Objects;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class TenantAwareEqualsHashCodeAdvice implements MethodInterceptor {
private final Object target;
private final String tenantIdentifier;
public TenantAwareEqualsHashCodeAdvice(Object target) {
this.target = target;
FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
this.tenantIdentifier = tenant != null ? tenant.getTenantIdentifier() : null;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String methodName = method.getName();
if ("equals".equals(methodName) && args.length == 1) {
Object other = args[0];
if (other instanceof Factory) {
TenantAwareEqualsHashCodeAdvice otherProxy = (TenantAwareEqualsHashCodeAdvice) ((Factory) other).getCallback(0);
return Objects.equals(target, otherProxy.target) && Objects.equals(tenantIdentifier, otherProxy.tenantIdentifier);
}
return false;
}
if ("hashCode".equals(methodName) && args.length == 0) {
return Objects.hash(target.hashCode(), tenantIdentifier);
}
return proxy.invoke(target, args);
}
}
@@ -0,0 +1,67 @@
/**
* 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.springframework.batch.core.scope.context;
import org.apache.fineract.infrastructure.jobs.TenantAwareEqualsHashCodeAdvice;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.lang.Nullable;
// Temporary solution until spring-batch fixes the concurrency issue
// https://github.com/spring-projects/spring-batch/issues/4774
// Mostly copy from spring-batch
@SuppressWarnings({ "HideUtilityClassConstructor" })
public class JobSynchronizationManager {
private static final SynchronizationManagerSupport<JobExecution, JobContext> manager = new SynchronizationManagerSupport<>() {
@Override
protected JobContext createNewContext(JobExecution execution) {
return new JobContext(execution);
}
@Override
protected void close(JobContext context) {
context.close();
}
};
@Nullable
public static JobContext getContext() {
return manager.getContext();
}
public static JobContext register(JobExecution jobExecution) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(JobExecution.class);
enhancer.setCallback(new TenantAwareEqualsHashCodeAdvice(jobExecution));
return manager.register((JobExecution) enhancer.create(new Class[] { JobInstance.class, Long.class, JobParameters.class },
new Object[] { jobExecution.getJobInstance(), jobExecution.getId(), jobExecution.getJobParameters() }));
}
public static void close() {
manager.close();
}
public static void release() {
manager.release();
}
}
@@ -0,0 +1,67 @@
/**
* 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.springframework.batch.core.scope.context;
import org.apache.fineract.infrastructure.jobs.TenantAwareEqualsHashCodeAdvice;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.StepExecution;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.lang.Nullable;
// Temporary solution until spring-batch fixes the concurrency issue
// https://github.com/spring-projects/spring-batch/issues/4774
// Mostly copy from spring-batch
@SuppressWarnings({ "HideUtilityClassConstructor" })
public class StepSynchronizationManager {
private static final SynchronizationManagerSupport<StepExecution, StepContext> manager = new SynchronizationManagerSupport<>() {
@Override
protected StepContext createNewContext(StepExecution execution) {
return new StepContext(execution);
}
@Override
protected void close(StepContext context) {
context.close();
}
};
@Nullable
public static StepContext getContext() {
return manager.getContext();
}
public static StepContext register(StepExecution stepExecution) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(StepExecution.class);
enhancer.setCallback(new TenantAwareEqualsHashCodeAdvice(stepExecution));
return manager.register((StepExecution) enhancer.create(new Class[] { String.class, JobExecution.class },
new Object[] { stepExecution.getStepName(), stepExecution.getJobExecution() }));
}
public static void close() {
manager.close();
}
public static void release() {
manager.release();
}
}