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}