001/* 002 * Copyright 2006-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.batch.core.repository.dao; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.Comparator; 022import java.util.List; 023import java.util.Map; 024import java.util.concurrent.ConcurrentHashMap; 025import java.util.concurrent.atomic.AtomicLong; 026 027import org.springframework.batch.core.DefaultJobKeyGenerator; 028import org.springframework.batch.core.JobExecution; 029import org.springframework.batch.core.JobInstance; 030import org.springframework.batch.core.JobKeyGenerator; 031import org.springframework.batch.core.JobParameters; 032import org.springframework.batch.core.launch.NoSuchJobException; 033import org.springframework.lang.Nullable; 034import org.springframework.util.Assert; 035 036/** 037 * In-memory implementation of {@link JobInstanceDao}. 038 */ 039public class MapJobInstanceDao implements JobInstanceDao { 040 private static final String STAR_WILDCARD = "\\*"; 041 private static final String STAR_WILDCARD_PATTERN = ".*"; 042 043 // JDK6 Make a ConcurrentSkipListSet: tends to add on end 044 private final Map<String, JobInstance> jobInstances = new ConcurrentHashMap<String, JobInstance>(); 045 046 private JobKeyGenerator<JobParameters> jobKeyGenerator = new DefaultJobKeyGenerator(); 047 048 private final AtomicLong currentId = new AtomicLong(0L); 049 050 public void clear() { 051 jobInstances.clear(); 052 } 053 054 @Override 055 public JobInstance createJobInstance(String jobName, JobParameters jobParameters) { 056 057 Assert.state(getJobInstance(jobName, jobParameters) == null, "JobInstance must not already exist"); 058 059 JobInstance jobInstance = new JobInstance(currentId.getAndIncrement(), jobName); 060 jobInstance.incrementVersion(); 061 jobInstances.put(jobName + "|" + jobKeyGenerator.generateKey(jobParameters), jobInstance); 062 063 return jobInstance; 064 } 065 066 @Override 067 @Nullable 068 public JobInstance getJobInstance(String jobName, JobParameters jobParameters) { 069 return jobInstances.get(jobName + "|" + jobKeyGenerator.generateKey(jobParameters)); 070 } 071 072 @Override 073 @Nullable 074 public JobInstance getJobInstance(@Nullable Long instanceId) { 075 for (Map.Entry<String, JobInstance> instanceEntry : jobInstances.entrySet()) { 076 JobInstance instance = instanceEntry.getValue(); 077 if (instance.getId().equals(instanceId)) { 078 return instance; 079 } 080 } 081 return null; 082 } 083 084 @Override 085 public List<String> getJobNames() { 086 List<String> result = new ArrayList<String>(); 087 for (Map.Entry<String, JobInstance> instanceEntry : jobInstances.entrySet()) { 088 result.add(instanceEntry.getValue().getJobName()); 089 } 090 Collections.sort(result); 091 return result; 092 } 093 094 @Override 095 public List<JobInstance> getJobInstances(String jobName, int start, int count) { 096 List<JobInstance> result = new ArrayList<JobInstance>(); 097 for (Map.Entry<String, JobInstance> instanceEntry : jobInstances.entrySet()) { 098 JobInstance instance = instanceEntry.getValue(); 099 if (instance.getJobName().equals(jobName)) { 100 result.add(instance); 101 } 102 } 103 104 sortDescending(result); 105 106 return subset(result, start, count); 107 } 108 109 @Override 110 @Nullable 111 public JobInstance getJobInstance(JobExecution jobExecution) { 112 return jobExecution.getJobInstance(); 113 } 114 115 @Override 116 public int getJobInstanceCount(@Nullable String jobName) throws NoSuchJobException { 117 int count = 0; 118 119 for (Map.Entry<String, JobInstance> instanceEntry : jobInstances.entrySet()) { 120 String key = instanceEntry.getKey(); 121 String curJobName = key.substring(0, key.lastIndexOf("|")); 122 123 if(curJobName.equals(jobName)) { 124 count++; 125 } 126 } 127 128 if(count == 0) { 129 throw new NoSuchJobException("No job instances for job name " + jobName + " were found"); 130 } else { 131 return count; 132 } 133 } 134 135 @Override 136 public List<JobInstance> findJobInstancesByName(String jobName, int start, int count) { 137 List<JobInstance> result = new ArrayList<JobInstance>(); 138 String convertedJobName = jobName.replaceAll(STAR_WILDCARD, STAR_WILDCARD_PATTERN); 139 140 for (Map.Entry<String, JobInstance> instanceEntry : jobInstances.entrySet()) { 141 JobInstance instance = instanceEntry.getValue(); 142 143 if(instance.getJobName().matches(convertedJobName)) { 144 result.add(instance); 145 } 146 } 147 148 sortDescending(result); 149 150 return subset(result, start, count); 151 } 152 153 private void sortDescending(List<JobInstance> result) { 154 Collections.sort(result, new Comparator<JobInstance>() { 155 @Override 156 public int compare(JobInstance o1, JobInstance o2) { 157 return Long.signum(o2.getId() - o1.getId()); 158 } 159 }); 160 } 161 162 private List<JobInstance> subset(List<JobInstance> jobInstances, int start, int count) { 163 int startIndex = Math.min(start, jobInstances.size()); 164 int endIndex = Math.min(start + count, jobInstances.size()); 165 166 return jobInstances.subList(startIndex, endIndex); 167 } 168}