001/* 002 * Copyright 2006-2007 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.repeat.context; 018 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025 026import org.springframework.batch.repeat.RepeatContext; 027 028public class RepeatContextSupport extends SynchronizedAttributeAccessor implements RepeatContext { 029 030 private RepeatContext parent; 031 032 private int count; 033 034 private volatile boolean completeOnly; 035 036 private volatile boolean terminateOnly; 037 038 private Map<String, Set<Runnable>> callbacks = new HashMap<String, Set<Runnable>>(); 039 040 /** 041 * Constructor for {@link RepeatContextSupport}. The parent can be null, but 042 * should be set to the enclosing repeat context if there is one, e.g. if 043 * this context is an inner loop. 044 * @param parent {@link RepeatContext} to be used as the parent context. 045 */ 046 public RepeatContextSupport(RepeatContext parent) { 047 super(); 048 this.parent = parent; 049 } 050 051 /* 052 * (non-Javadoc) 053 * 054 * @see org.springframework.batch.repeat.RepeatContext#isCompleteOnly() 055 */ 056 @Override 057 public boolean isCompleteOnly() { 058 return completeOnly; 059 } 060 061 /* 062 * (non-Javadoc) 063 * 064 * @see org.springframework.batch.repeat.RepeatContext#setCompleteOnly() 065 */ 066 @Override 067 public void setCompleteOnly() { 068 completeOnly = true; 069 } 070 071 /* 072 * (non-Javadoc) 073 * 074 * @see org.springframework.batch.repeat.RepeatContext#isTerminateOnly() 075 */ 076 @Override 077 public boolean isTerminateOnly() { 078 return terminateOnly; 079 } 080 081 /* 082 * (non-Javadoc) 083 * 084 * @see org.springframework.batch.repeat.RepeatContext#setTerminateOnly() 085 */ 086 @Override 087 public void setTerminateOnly() { 088 terminateOnly = true; 089 setCompleteOnly(); 090 } 091 092 /* 093 * (non-Javadoc) 094 * 095 * @see org.springframework.batch.repeat.RepeatContext#getParent() 096 */ 097 @Override 098 public RepeatContext getParent() { 099 return parent; 100 } 101 102 /** 103 * Used by clients to increment the started count. 104 */ 105 public synchronized void increment() { 106 count++; 107 } 108 109 /* 110 * (non-Javadoc) 111 * 112 * @see org.springframework.batch.repeat.RepeatContext#getStartedCount() 113 */ 114 @Override 115 public synchronized int getStartedCount() { 116 return count; 117 } 118 119 /* 120 * (non-Javadoc) 121 * 122 * @see 123 * org.springframework.batch.repeat.RepeatContext#registerDestructionCallback 124 * (java.lang.String, java.lang.Runnable) 125 */ 126 @Override 127 public void registerDestructionCallback(String name, Runnable callback) { 128 synchronized (callbacks) { 129 Set<Runnable> set = callbacks.get(name); 130 if (set == null) { 131 set = new HashSet<Runnable>(); 132 callbacks.put(name, set); 133 } 134 set.add(callback); 135 } 136 } 137 138 /* 139 * (non-Javadoc) 140 * 141 * @see org.springframework.batch.repeat.RepeatContext#close() 142 */ 143 @Override 144 public void close() { 145 146 List<RuntimeException> errors = new ArrayList<RuntimeException>(); 147 148 Set<Map.Entry<String, Set<Runnable>>> copy; 149 150 synchronized (callbacks) { 151 copy = new HashSet<Map.Entry<String, Set<Runnable>>>(callbacks.entrySet()); 152 } 153 154 for (Map.Entry<String, Set<Runnable>> entry : copy) { 155 156 for (Runnable callback : entry.getValue()) { 157 /* 158 * Potentially we could check here if there is an attribute with 159 * the given name - if it has been removed, maybe the callback 160 * is invalid. On the other hand it is less surprising for the 161 * callback register if it is always executed. 162 */ 163 if (callback != null) { 164 /* 165 * The documentation of the interface says that these 166 * callbacks must not throw exceptions, but we don't trust 167 * them necessarily... 168 */ 169 try { 170 callback.run(); 171 } 172 catch (RuntimeException t) { 173 errors.add(t); 174 } 175 } 176 } 177 } 178 179 if (errors.isEmpty()) { 180 return; 181 } 182 183 throw errors.get(0); 184 } 185 186}