/*
* Copyright (c) 2012-2013 Red Hat, Inc. and/or its affiliates.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Cheng Fang - Initial API and implementation
*/
package org.jberet.repository;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
import javax.batch.runtime.BatchStatus;
import javax.batch.runtime.JobExecution;
import javax.batch.runtime.JobInstance;
import javax.batch.runtime.StepExecution;
import org.jberet._private.BatchLogger;
import org.jberet.job.model.Job;
import org.jberet.runtime.JobExecutionImpl;
import org.jberet.runtime.JobInstanceImpl;
import org.jberet.runtime.StepExecutionImpl;
public final class InMemoryRepository extends AbstractRepository {
private final ConcurrentMap<Long, JobInstanceImpl> jobInstances = new ConcurrentHashMap<Long, JobInstanceImpl>();
private final ConcurrentMap<Long, JobExecutionImpl> jobExecutions = new ConcurrentHashMap<Long, JobExecutionImpl>();
private final AtomicLong jobInstanceIdSequence = new AtomicLong();
private final AtomicLong jobExecutionIdSequence = new AtomicLong();
private final AtomicLong stepExecutionIdSequence = new AtomicLong();
public InMemoryRepository() {
}
private static class Holder {
private static final InMemoryRepository instance = new InMemoryRepository();
}
/**
* Gets a singleton instance of an in-memory job repository.
*
* @return an in-memory job repository
*/
public static InMemoryRepository getInstance() {
return Holder.instance;
}
/**
* Creates a new in-memory job repository.
* Use where multiple in-memory job repositories may be needed.
*
* @return a new in-memory job repository
*/
public static InMemoryRepository create() {
return new InMemoryRepository();
}
@Override
public void removeJob(final String jobId) {
super.removeJob(jobId);
//perform cascade delete
for (final Iterator<Map.Entry<Long, JobInstanceImpl>> it = jobInstances.entrySet().iterator(); it.hasNext(); ) {
final JobInstance ji = it.next().getValue();
if (ji.getJobName().equals(jobId)) {
BatchLogger.LOGGER.removing(JobInstance.class.getName(), String.valueOf(ji.getInstanceId()));
it.remove();
}
}
for (final Iterator<Map.Entry<Long, JobExecutionImpl>> it = jobExecutions.entrySet().iterator(); it.hasNext(); ) {
final JobExecution je = it.next().getValue();
if (je.getJobName().equals(jobId)) {
if (je.getJobParameters() != null) {
je.getJobParameters().clear();
}
BatchLogger.LOGGER.removing(JobExecution.class.getName(), String.valueOf(je.getExecutionId()));
it.remove();
}
}
}
@Override
void insertJobInstance(final JobInstanceImpl jobInstance) {
jobInstance.setId(jobInstanceIdSequence.incrementAndGet());
}
@Override
void insertJobExecution(final JobExecutionImpl jobExecution) {
jobExecution.setId(jobExecutionIdSequence.incrementAndGet());
}
@Override
void insertStepExecution(final StepExecutionImpl stepExecution, final JobExecutionImpl jobExecution) {
stepExecution.setId(stepExecutionIdSequence.incrementAndGet());
}
@Override
public void updateStepExecution(final StepExecution stepExecution) {
// do nothing
}
@Override
public int countStepStartTimes(final String stepName, final long jobInstanceId) {
int count = 0;
final JobInstanceImpl jobInstanceImpl = jobInstances.get(jobInstanceId);
if (jobInstanceImpl != null) {
for (final JobExecution jobExecution : jobInstanceImpl.getJobExecutions()) {
final JobExecutionImpl jobExecutionImpl = (JobExecutionImpl) jobExecution;
for (final StepExecution stepExecution : jobExecutionImpl.getStepExecutions()) {
if (stepExecution.getStepName().equals(stepName)) {
count++;
}
}
}
}
return count;
}
@Override
public void removeJobExecutions(final JobExecutionSelector jobExecutionSelector) {
final Collection<Long> allJobExecutionIds = jobExecutions.keySet();
for (final Iterator<Map.Entry<Long, JobExecutionImpl>> it = jobExecutions.entrySet().iterator(); it.hasNext(); ) {
final JobExecutionImpl je = it.next().getValue();
if (jobExecutionSelector == null || jobExecutionSelector.select(je, allJobExecutionIds)) {
if (je.getJobParameters() != null) {
je.getJobParameters().clear();
}
BatchLogger.LOGGER.removing(JobExecution.class.getName(), String.valueOf(je.getExecutionId()));
it.remove();
}
}
}
@Override
public JobInstanceImpl createJobInstance(final Job job, final String applicationName, final ClassLoader classLoader) {
final JobInstanceImpl jobInstance = new JobInstanceImpl(job, applicationName, job.getId());
insertJobInstance(jobInstance);
jobInstances.put(jobInstance.getInstanceId(), jobInstance);
return jobInstance;
}
@Override
public void removeJobInstance(final long jobInstanceIdToRemove) {
BatchLogger.LOGGER.removing(JobInstance.class.getName(), String.valueOf(jobInstanceIdToRemove));
jobInstances.remove(jobInstanceIdToRemove);
}
@Override
public JobInstance getJobInstance(final long jobInstanceId) {
return jobInstances.get(jobInstanceId);
}
@Override
public List<JobInstance> getJobInstances(final String jobName) {
final List<JobInstance> result = new ArrayList<JobInstance>();
final long largestJobInstanceId = jobInstanceIdSequence.get();
final boolean selectAll = jobName == null || jobName.equals("*");
for (long i = largestJobInstanceId; i > 0; i--) {
final JobInstanceImpl e = jobInstances.get(i);
if (e != null && (selectAll || jobName.equals(e.getJobName()))) {
result.add(e);
}
}
return result;
}
@Override
public int getJobInstanceCount(final String jobName) {
int count = 0;
for (final JobInstance e : jobInstances.values()) {
if (e.getJobName().equals(jobName)) {
count++;
}
}
return count;
}
@Override
public JobExecutionImpl createJobExecution(final JobInstanceImpl jobInstance, final Properties jobParameters) {
final JobExecutionImpl jobExecution = new JobExecutionImpl(jobInstance, jobParameters);
insertJobExecution(jobExecution);
jobExecutions.put(jobExecution.getExecutionId(), jobExecution);
jobInstance.addJobExecution(jobExecution);
return jobExecution;
}
@Override
public JobExecutionImpl getJobExecution(final long jobExecutionId) {
return jobExecutions.get(jobExecutionId);
}
@Override
public List<JobExecution> getJobExecutions(final JobInstance jobInstance) {
if (jobInstance == null) {
//return all JobExecution
final List<JobExecution> result = new ArrayList<JobExecution>();
result.addAll(this.jobExecutions.values());
return result;
} else {
return ((JobInstanceImpl) jobInstance).getJobExecutions();
}
}
@Override
public List<StepExecution> getStepExecutions(final long jobExecutionId, final ClassLoader classLoader) {
final JobExecutionImpl jobExecution = getJobExecution(jobExecutionId);
if (jobExecution == null) {
return Collections.emptyList();
}
return jobExecution.getStepExecutions();
}
@Override
public StepExecutionImpl findOriginalStepExecutionForRestart(final String stepName,
final JobExecutionImpl jobExecutionToRestart,
final ClassLoader classLoader) {
for (final StepExecution stepExecution : jobExecutionToRestart.getStepExecutions()) {
if (stepName.equals(stepExecution.getStepName())) {
return (StepExecutionImpl) stepExecution;
}
}
StepExecutionImpl result = null;
// the same-named StepExecution is not found in the jobExecutionToRestart. It's still possible the same-named
// StepExecution may exit in JobExecution earlier than jobExecutionToRestart for the same JobInstance.
final long instanceId = jobExecutionToRestart.getJobInstance().getInstanceId();
for (final JobExecutionImpl jobExecutionImpl : jobExecutions.values()) {
//skip the JobExecution that has already been checked above
if (instanceId == jobExecutionImpl.getJobInstance().getInstanceId() &&
jobExecutionImpl.getExecutionId() != jobExecutionToRestart.getExecutionId()) {
for (final StepExecution stepExecution : jobExecutionImpl.getStepExecutions()) {
if (stepExecution.getStepName().equals(stepName)) {
if (result == null || result.getStepExecutionId() < stepExecution.getStepExecutionId()) {
result = (StepExecutionImpl) stepExecution;
}
}
}
}
}
return result;
}
@Override
public List<Long> getRunningExecutions(final String jobName) {
final List<Long> result = new ArrayList<Long>();
for (final Map.Entry<Long, JobExecutionImpl> e : jobExecutions.entrySet()) {
if (e.getValue().getJobName().equals(jobName)) {
final BatchStatus s = e.getValue().getBatchStatus();
if (s == BatchStatus.STARTING || s == BatchStatus.STARTED) {
result.add(e.getKey());
}
}
}
return result;
}
}