/* * Copyright (c) 2014-2015 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.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import javax.batch.runtime.BatchStatus; import javax.batch.runtime.JobExecution; import javax.batch.runtime.JobInstance; import javax.batch.runtime.StepExecution; import javax.transaction.TransactionManager; import org.infinispan.Cache; import org.infinispan.configuration.cache.Configuration; import org.infinispan.context.Flag; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.manager.EmbeddedCacheManager; import org.jberet._private.BatchLogger; import org.jberet._private.BatchMessages; import org.jberet.job.model.Job; import org.jberet.runtime.AbstractStepExecution; import org.jberet.runtime.JobExecutionImpl; import org.jberet.runtime.JobInstanceImpl; import org.jberet.runtime.PartitionExecutionImpl; import org.jberet.runtime.StepExecutionImpl; import org.jberet.spi.PropertyKey; public final class InfinispanRepository extends AbstractRepository { private static final String DEFAULT_INFINISPAN_XML = "infinispan.xml"; private Cache<String, Long> sequenceCache; private Cache<Long, JobInstanceImpl> jobInstanceCache; private Cache<Long, JobExecutionImpl> jobExecutionCache; private Cache<Long, StepExecutionImpl> stepExecutionCache; private Cache<String, PartitionExecutionImpl> partitionExecutionCache; private final EmbeddedCacheManager cacheManager; public static InfinispanRepository create(final Configuration infinispanConfig) { return new InfinispanRepository(infinispanConfig); } public static InfinispanRepository create(final Properties configProperties) { String infinispanXml = configProperties.getProperty(PropertyKey.INFINISPAN_XML); if (infinispanXml == null || infinispanXml.isEmpty()) { infinispanXml = DEFAULT_INFINISPAN_XML; } return new InfinispanRepository(infinispanXml); } public InfinispanRepository(final Configuration infinispanConfig) { cacheManager = new DefaultCacheManager(infinispanConfig); initCaches(); } public InfinispanRepository(final String infinispanXml) { try { cacheManager = new DefaultCacheManager(infinispanXml); } catch (final IOException e) { throw BatchMessages.MESSAGES.failToCreateCacheManager(e, infinispanXml); } initCaches(); } @Override public void removeJob(final String jobId) { super.removeJob(jobId); //do not cascade-remove JobInstance or JobExecution, since it will be too expensive in a distributed cache, and //also too destructive to remove all these historical records. } @Override public JobInstanceImpl createJobInstance(final Job job, final String applicationName, final ClassLoader classLoader) { final JobInstanceImpl jobInstance = new JobInstanceImpl(job, applicationName, job.getId()); insertJobInstance(jobInstance); return jobInstance; } @Override public void removeJobInstance(final long jobInstanceIdToRemove) { jobInstanceCache.remove(jobInstanceIdToRemove); } @Override public void removeJobExecutions(final JobExecutionSelector jobExecutionSelector) { final Collection<Long> allJobExecutionIds = jobExecutionCache.keySet(); for (final Iterator<Map.Entry<Long, JobExecutionImpl>> it = jobExecutionCache.entrySet().iterator(); it.hasNext(); ) { final JobExecutionImpl je = it.next().getValue(); if (je != null && (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 JobInstance getJobInstance(final long jobInstanceId) { return jobInstanceCache.get(jobInstanceId); } @Override public List<JobInstance> getJobInstances(final String jobName) { final List<JobInstance> result = new ArrayList<JobInstance>(); final long largestJobInstanceId = sequenceCache.get(TableColumns.JOB_INSTANCE_ID_SEQ); final boolean selectAll = jobName == null || jobName.equals("*"); for (long i = largestJobInstanceId; i > 0; i--) { final JobInstanceImpl e = jobInstanceCache.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; final long largestJobInstanceId = sequenceCache.get(TableColumns.JOB_INSTANCE_ID_SEQ); for (long i = largestJobInstanceId; i > 0; i--) { final JobInstanceImpl e = jobInstanceCache.get(i); if (e != null && 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); jobInstance.addJobExecution(jobExecution); return jobExecution; } @Override public JobExecutionImpl getJobExecution(final long jobExecutionId) { return jobExecutionCache.get(jobExecutionId); } @Override public List<JobExecution> getJobExecutions(final JobInstance jobInstance) { List<JobExecution> result; if (jobInstance == null) { //return all JobExecution result = new ArrayList<JobExecution>(); result.addAll(jobExecutionCache.values()); } else { final long instanceId = jobInstance.getInstanceId(); result = jobInstanceCache.get(instanceId).getJobExecutions(); if (result.isEmpty()) { result = new ArrayList<JobExecution>(); for (final JobExecutionImpl e : jobExecutionCache.values()) { if (instanceId == e.getJobInstance().getInstanceId()) { result.add(e); } } } } return result; } @Override public List<StepExecution> getStepExecutions(final long jobExecutionId, final ClassLoader classLoader) { final JobExecutionImpl jobExecution = jobExecutionCache.get(jobExecutionId); return jobExecution.getStepExecutions(); } @Override public void savePersistentData(final JobExecution jobExecution, final AbstractStepExecution stepOrPartitionExecution) { //super.savePersistentData() serialize persistent data and checkpoint info to avoid further modification super.savePersistentData(jobExecution, stepOrPartitionExecution); if (stepOrPartitionExecution instanceof StepExecutionImpl) { updateStepExecution(stepOrPartitionExecution); } else { final PartitionExecutionImpl partitionExecution = (PartitionExecutionImpl) stepOrPartitionExecution; partitionExecutionCache.put( concatPartitionExecutionId(partitionExecution.getStepExecutionId(), partitionExecution.getPartitionId()), partitionExecution); } } @Override public void updateStepExecution(final StepExecution stepExecution) { stepExecutionCache.put(stepExecution.getStepExecutionId(), (StepExecutionImpl) stepExecution); } @Override public void updateJobExecution(final JobExecutionImpl jobExecution, final boolean fullUpdate, final boolean saveJobParameters) { jobExecution.setLastUpdatedTime(System.currentTimeMillis()); jobExecutionCache.put(jobExecution.getExecutionId(), jobExecution); } @Override public StepExecutionImpl findOriginalStepExecutionForRestart(final String stepName, final JobExecutionImpl jobExecutionToRestart0, final ClassLoader classLoader) { final JobExecutionImpl jobExecutionToRestart = jobExecutionCache.get(jobExecutionToRestart0.getExecutionId()); 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(); final JobInstanceImpl jobInstanceToRestart = jobInstanceCache.get(instanceId); for (final JobExecution jobExecution : jobInstanceToRestart.getJobExecutions()) { final JobExecutionImpl jobExecutionImpl = (JobExecutionImpl) jobExecution; //skip the JobExecution that has already been checked above if (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 void addPartitionExecution(final StepExecutionImpl enclosingStepExecution, final PartitionExecutionImpl partitionExecution) { super.addPartitionExecution(enclosingStepExecution, partitionExecution); partitionExecutionCache.put( concatPartitionExecutionId(partitionExecution.getStepExecutionId(), partitionExecution.getPartitionId()), partitionExecution); } @Override public List<PartitionExecutionImpl> getPartitionExecutions(final long stepExecutionId, final StepExecutionImpl stepExecution, final boolean notCompletedOnly, final ClassLoader classLoader) { return super.getPartitionExecutions(stepExecutionId, stepExecutionCache.get(stepExecutionId), notCompletedOnly, classLoader); } @Override public int countStepStartTimes(final String stepName, final long jobInstanceId) { int count = 0; final JobInstanceImpl jobInstanceImpl = jobInstanceCache.get(jobInstanceId); if (jobInstanceImpl != null) { for (final JobExecution jobExecution : jobInstanceImpl.getJobExecutions()) { final JobExecutionImpl jobExecutionImpl = jobExecutionCache.get(jobExecution.getExecutionId()); for (final StepExecution stepExecution : jobExecutionImpl.getStepExecutions()) { if (stepExecution.getStepName().equals(stepName)) { count++; } } } } return count; } @Override public List<Long> getRunningExecutions(final String jobName) { final List<Long> result = new ArrayList<Long>(); for (final Map.Entry<Long, JobExecutionImpl> e : jobExecutionCache.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; } @Override void insertJobInstance(final JobInstanceImpl jobInstance) { final long jobInstanceId = getNextIdFor(TableColumns.JOB_INSTANCE_ID_SEQ); jobInstance.setId(jobInstanceId); jobInstanceCache.put(jobInstanceId, jobInstance); } @Override void insertJobExecution(final JobExecutionImpl jobExecution) { final Long jobExecutionId = getNextIdFor(TableColumns.JOB_EXECUTION_ID_SEQ); jobExecution.setId(jobExecutionId); jobExecutionCache.put(jobExecutionId, jobExecution); } @Override void insertStepExecution(final StepExecutionImpl stepExecution, final JobExecutionImpl jobExecution) { final long stepExecutionId = getNextIdFor(TableColumns.STEP_EXECUTION_ID_SEQ); stepExecution.setId(stepExecutionId); stepExecutionCache.put(stepExecutionId, stepExecution); } private void initCaches() { sequenceCache = cacheManager.getCache(TableColumns.SEQ, true); sequenceCache.getAdvancedCache().withFlags(Flag.SKIP_REMOTE_LOOKUP, Flag.SKIP_CACHE_LOAD); sequenceCache.putIfAbsent(TableColumns.JOB_INSTANCE_ID_SEQ, 0L); sequenceCache.putIfAbsent(TableColumns.JOB_EXECUTION_ID_SEQ, 0L); sequenceCache.putIfAbsent(TableColumns.STEP_EXECUTION_ID_SEQ, 0L); jobInstanceCache = cacheManager.getCache(TableColumns.JOB_INSTANCE, true); jobInstanceCache.getAdvancedCache().withFlags(Flag.SKIP_REMOTE_LOOKUP, Flag.SKIP_CACHE_LOAD); jobExecutionCache = cacheManager.getCache(TableColumns.JOB_EXECUTION, true); jobExecutionCache.getAdvancedCache().withFlags(Flag.SKIP_REMOTE_LOOKUP, Flag.SKIP_CACHE_LOAD); stepExecutionCache = cacheManager.getCache(TableColumns.STEP_EXECUTION, true); stepExecutionCache.getAdvancedCache().withFlags(Flag.SKIP_REMOTE_LOOKUP, Flag.SKIP_CACHE_LOAD); partitionExecutionCache = cacheManager.getCache(TableColumns.PARTITION_EXECUTION, true); partitionExecutionCache.getAdvancedCache().withFlags(Flag.SKIP_REMOTE_LOOKUP, Flag.SKIP_CACHE_LOAD); } private long getNextIdFor(final String key) { final long nextId; final TransactionManager infinispanTransactionManager = sequenceCache.getAdvancedCache().getTransactionManager(); try { infinispanTransactionManager.begin(); sequenceCache.getAdvancedCache().lock(key); nextId = sequenceCache.get(key) + 1; sequenceCache.put(key, nextId); infinispanTransactionManager.commit(); return nextId; } catch (final Exception e) { throw BatchMessages.MESSAGES.failToGetNextId(e, key); } } private static String concatPartitionExecutionId(final long stepExecutionId, final int partitionId) { return (new StringBuilder(String.valueOf(stepExecutionId)).append('-').append(String.valueOf(partitionId))).toString(); } }