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}