/* Licensed 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.activiti.engine.impl.asyncexecutor.multitenant;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import org.activiti.engine.impl.asyncexecutor.AsyncExecutor;
import org.activiti.engine.impl.asyncexecutor.DefaultAsyncJobExecutor;
import org.activiti.engine.impl.asyncexecutor.ExecuteAsyncRunnableFactory;
import org.activiti.engine.impl.cfg.multitenant.TenantInfoHolder;
import org.activiti.engine.impl.interceptor.CommandExecutor;
import org.activiti.engine.impl.persistence.entity.JobEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Multi tenant {@link AsyncExecutor}.
*
* For each tenant, there will be acquire threads, but only one {@link ExecutorService} will be used
* once the jobs are acquired.
*
* @author Joram Barrez
*/
public class SharedExecutorServiceAsyncExecutor extends DefaultAsyncJobExecutor implements TenantAwareAsyncExecutor {
private static final Logger logger = LoggerFactory.getLogger(SharedExecutorServiceAsyncExecutor.class);
protected TenantInfoHolder tenantInfoHolder;
protected Map<String, Thread> timerJobAcquisitionThreads = new HashMap<String, Thread>();
protected Map<String, TenantAwareAcquireTimerJobsRunnable> timerJobAcquisitionRunnables
= new HashMap<String, TenantAwareAcquireTimerJobsRunnable>();
protected Map<String, Thread> asyncJobAcquisitionThreads = new HashMap<String, Thread>();
protected Map<String, TenantAwareAcquireAsyncJobsDueRunnable> asyncJobAcquisitionRunnables
= new HashMap<String, TenantAwareAcquireAsyncJobsDueRunnable>();
public SharedExecutorServiceAsyncExecutor(TenantInfoHolder tenantInfoHolder) {
this.tenantInfoHolder = tenantInfoHolder;
setExecuteAsyncRunnableFactory(new ExecuteAsyncRunnableFactory() {
public Runnable createExecuteAsyncRunnable(JobEntity jobEntity, CommandExecutor commandExecutor) {
// Here, the runnable will be created by for example the acquire thread, which has already set the current id.
// But it will be executed later on, by the executorService and thus we need to set it explicitely again then
return new TenantAwareExecuteAsyncRunnable(jobEntity, commandExecutor,
SharedExecutorServiceAsyncExecutor.this.tenantInfoHolder,
SharedExecutorServiceAsyncExecutor.this.tenantInfoHolder.getCurrentTenantId());
}
});
}
@Override
public Set<String> getTenantIds() {
return timerJobAcquisitionThreads.keySet();
}
public void addTenantAsyncExecutor(String tenantId, boolean startExecutor) {
TenantAwareAcquireTimerJobsRunnable timerRunnable = new TenantAwareAcquireTimerJobsRunnable(this, tenantInfoHolder, tenantId);
timerJobAcquisitionRunnables.put(tenantId, timerRunnable);
timerJobAcquisitionThreads.put(tenantId, new Thread(timerRunnable));
TenantAwareAcquireAsyncJobsDueRunnable asyncJobsRunnable = new TenantAwareAcquireAsyncJobsDueRunnable(this, tenantInfoHolder, tenantId);
asyncJobAcquisitionRunnables.put(tenantId, asyncJobsRunnable);
asyncJobAcquisitionThreads.put(tenantId, new Thread(asyncJobsRunnable));
if (startExecutor) {
startTimerJobAcquisitionForTenant(tenantId);
startAsyncJobAcquisitionForTenant(tenantId);
}
}
@Override
public void removeTenantAsyncExecutor(String tenantId) {
stopThreadsForTenant(tenantId);
}
@Override
protected void startJobAcquisitionThread() {
for (String tenantId : timerJobAcquisitionThreads.keySet()) {
startTimerJobAcquisitionForTenant(tenantId);
}
for (String tenantId : asyncJobAcquisitionThreads.keySet()) {
asyncJobAcquisitionThreads.get(tenantId).start();
}
}
protected void startTimerJobAcquisitionForTenant(String tenantId) {
timerJobAcquisitionThreads.get(tenantId).start();
}
protected void startAsyncJobAcquisitionForTenant(String tenantId) {
asyncJobAcquisitionThreads.get(tenantId).start();
}
@Override
protected void stopJobAcquisitionThread() {
for (String tenantId : timerJobAcquisitionRunnables.keySet()) {
stopThreadsForTenant(tenantId);
}
}
protected void stopThreadsForTenant(String tenantId) {
timerJobAcquisitionRunnables.get(tenantId).stop();
asyncJobAcquisitionRunnables.get(tenantId).stop();
try {
timerJobAcquisitionThreads.get(tenantId).join();
} catch (InterruptedException e) {
logger.warn("Interrupted while waiting for the timer job acquisition thread to terminate", e);
}
try {
asyncJobAcquisitionThreads.get(tenantId).join();
} catch (InterruptedException e) {
logger.warn("Interrupted while waiting for the timer job acquisition thread to terminate", e);
}
}
}