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}