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.io.Serializable;
020import java.util.Collection;
021import java.util.concurrent.ConcurrentMap;
022
023import org.springframework.batch.core.JobExecution;
024import org.springframework.batch.core.StepExecution;
025import org.springframework.batch.item.ExecutionContext;
026import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
027import org.springframework.util.Assert;
028import org.springframework.util.SerializationUtils;
029
030/**
031 * In-memory implementation of {@link ExecutionContextDao} backed by maps.
032 *
033 * @author Robert Kasanicky
034 * @author Dave Syer
035 * @author David Turanski
036 * @author Mahmoud Ben Hassine
037 */
038@SuppressWarnings("serial")
039public class MapExecutionContextDao implements ExecutionContextDao {
040
041        private final ConcurrentMap<ContextKey, ExecutionContext> contexts = TransactionAwareProxyFactory
042                        .createAppendOnlyTransactionalMap();
043
044        private static final class ContextKey implements Comparable<ContextKey>, Serializable {
045
046                private static enum Type { STEP, JOB; }
047
048                private final Type type;
049                private final long id;
050
051                private ContextKey(Type type, long id) {
052                        if(type == null) {
053                                throw new IllegalStateException("Need a non-null type for a context");
054                        }
055                        this.type = type;
056                        this.id = id;
057                }
058
059                @Override
060                public int compareTo(ContextKey them) {
061                        if(them == null) {
062                                return 1;
063                        }
064                        final int idCompare = new Long(this.id).compareTo(new Long(them.id)); // JDK6 Make this Long.compare(x,y)
065                        if(idCompare != 0) {
066                                return idCompare;
067                        }
068                        final int typeCompare = this.type.compareTo(them.type);
069                        if(typeCompare != 0) {
070                                return typeCompare;
071                        }
072                        return 0;
073                }
074
075                @Override
076                public boolean equals(Object them) {
077                        if(them == null) {
078                                return false;
079                        }
080                        if(them instanceof ContextKey) {
081                                return this.equals((ContextKey)them);
082                        }
083                        return false;
084                }
085
086                public boolean equals(ContextKey them) {
087                        if(them == null) {
088                                return false;
089                        }
090                        return this.id == them.id && this.type.equals(them.type);
091                }
092
093                @Override
094                public int hashCode() {
095                        int value = (int)(id^(id>>>32));
096                        switch(type) {
097                        case STEP: return value;
098                        case JOB: return ~value;
099                        default: throw new IllegalStateException("Unknown type encountered in switch: " + type);
100                        }
101                }
102
103                public static ContextKey step(long id) { return new ContextKey(Type.STEP, id); }
104
105                public static ContextKey job(long id) { return new ContextKey(Type.JOB, id); }
106        }
107
108        public void clear() {
109                contexts.clear();
110        }
111
112        private static ExecutionContext copy(ExecutionContext original) {
113                return (ExecutionContext) SerializationUtils.deserialize(SerializationUtils.serialize(original));
114        }
115
116        @Override
117        public ExecutionContext getExecutionContext(StepExecution stepExecution) {
118                return copy(contexts.get(ContextKey.step(stepExecution.getId())));
119        }
120
121        @Override
122        public void updateExecutionContext(StepExecution stepExecution) {
123                ExecutionContext executionContext = stepExecution.getExecutionContext();
124                if (executionContext != null) {
125                        contexts.put(ContextKey.step(stepExecution.getId()), copy(executionContext));
126                }
127        }
128
129        @Override
130        public ExecutionContext getExecutionContext(JobExecution jobExecution) {
131                return copy(contexts.get(ContextKey.job(jobExecution.getId())));
132        }
133
134        @Override
135        public void updateExecutionContext(JobExecution jobExecution) {
136                ExecutionContext executionContext = jobExecution.getExecutionContext();
137                if (executionContext != null) {
138                        contexts.put(ContextKey.job(jobExecution.getId()), copy(executionContext));
139                }
140        }
141
142        @Override
143        public void saveExecutionContext(JobExecution jobExecution) {
144                updateExecutionContext(jobExecution);
145        }
146
147        @Override
148        public void saveExecutionContext(StepExecution stepExecution) {
149                updateExecutionContext(stepExecution);
150        }
151
152
153        @Override
154        public void saveExecutionContexts(Collection<StepExecution> stepExecutions) {
155                Assert.notNull(stepExecutions,"Attempt to save a null collection of step executions");
156                for (StepExecution stepExecution: stepExecutions) {
157                        saveExecutionContext(stepExecution);
158                        saveExecutionContext(stepExecution.getJobExecution());
159                }
160        }
161
162}