EMMA Coverage Report (generated Tue May 06 07:28:24 PDT 2008)
[all classes][org.springframework.batch.retry.policy]

COVERAGE SUMMARY FOR SOURCE FILE [ItemWriterRetryPolicy.java]

nameclass, %method, %block, %line, %
ItemWriterRetryPolicy.java100% (2/2)90%  (18/20)87%  (225/260)91%  (51.9/57)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ItemWriterRetryPolicy$ItemWriterRetryContext100% (1/1)80%  (8/10)80%  (136/170)86%  (30/35)
isExternal (): boolean 0%   (0/1)0%   (0/5)0%   (0/1)
shouldRethrow (RetryContext): boolean 0%   (0/1)0%   (0/5)0%   (0/1)
handleRetryExhausted (RetryContext): Object 100% (1/1)55%  (23/42)75%  (6/8)
open (RetryCallback, RetryContext): RetryContext 100% (1/1)85%  (29/34)83%  (5/6)
ItemWriterRetryPolicy$ItemWriterRetryContext (ItemWriterRetryPolicy, ItemWrit... 100% (1/1)100% (35/35)100% (9/9)
canRetry (RetryContext): boolean 100% (1/1)100% (7/7)100% (1/1)
close (RetryContext): void 100% (1/1)100% (7/7)100% (2/2)
getLastThrowable (): Throwable 100% (1/1)100% (4/4)100% (1/1)
getRetryCount (): int 100% (1/1)100% (4/4)100% (1/1)
registerThrowable (RetryContext, Throwable): void 100% (1/1)100% (27/27)100% (5/5)
     
class ItemWriterRetryPolicy100% (1/1)100% (10/10)99%  (89/90)100% (21.9/22)
<static initializer> 100% (1/1)94%  (17/18)94%  (0.9/1)
ItemWriterRetryPolicy (): void 100% (1/1)100% (6/6)100% (2/2)
ItemWriterRetryPolicy (RetryPolicy): void 100% (1/1)100% (11/11)100% (4/4)
canRetry (RetryContext): boolean 100% (1/1)100% (5/5)100% (1/1)
close (RetryContext): void 100% (1/1)100% (5/5)100% (2/2)
handleRetryExhausted (RetryContext): Object 100% (1/1)100% (5/5)100% (1/1)
hasFailed (FailedItemIdentifier, Object): boolean 100% (1/1)100% (11/11)100% (3/3)
open (RetryCallback, RetryContext): RetryContext 100% (1/1)100% (19/19)100% (4/4)
registerThrowable (RetryContext, Throwable): void 100% (1/1)100% (6/6)100% (2/2)
setDelegate (RetryPolicy): void 100% (1/1)100% (4/4)100% (2/2)

1/*
2 * Copyright 2006-2007 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 
17package org.springframework.batch.retry.policy;
18 
19import org.apache.commons.logging.Log;
20import org.apache.commons.logging.LogFactory;
21import org.springframework.batch.item.FailedItemIdentifier;
22import org.springframework.batch.item.ItemKeyGenerator;
23import org.springframework.batch.item.ItemRecoverer;
24import org.springframework.batch.repeat.support.RepeatSynchronizationManager;
25import org.springframework.batch.retry.RetryCallback;
26import org.springframework.batch.retry.RetryContext;
27import org.springframework.batch.retry.RetryException;
28import org.springframework.batch.retry.RetryPolicy;
29import org.springframework.batch.retry.TerminatedRetryException;
30import org.springframework.batch.retry.callback.ItemWriterRetryCallback;
31import org.springframework.batch.retry.context.RetryContextSupport;
32import org.springframework.util.Assert;
33 
34/**
35 * A {@link RetryPolicy} that detects an {@link ItemWriterRetryCallback} when it
36 * opens a new context, and uses it to make sure the item is in place for later
37 * decisions about how to retry or backoff. The callback should be an instance
38 * of {@link ItemWriterRetryCallback} otherwise an exception will be thrown when
39 * the context is created.
40 * 
41 * @author Dave Syer
42 * 
43 */
44public class ItemWriterRetryPolicy extends AbstractStatefulRetryPolicy {
45 
46        protected Log logger = LogFactory.getLog(getClass());
47 
48        public static final String EXHAUSTED = ItemWriterRetryPolicy.class.getName() + ".EXHAUSTED";
49 
50        private RetryPolicy delegate;
51 
52        /**
53         * Convenience constructor to set delegate on init.
54         * 
55         * @param delegate
56         */
57        public ItemWriterRetryPolicy(RetryPolicy delegate) {
58                super();
59                this.delegate = delegate;
60        }
61 
62        /**
63         * Default constructor. Creates a new {@link SimpleRetryPolicy} for the
64         * delegate.
65         */
66        public ItemWriterRetryPolicy() {
67                this(new SimpleRetryPolicy());
68        }
69 
70        /**
71         * Setter for delegate.
72         * 
73         * @param delegate
74         */
75        public void setDelegate(RetryPolicy delegate) {
76                this.delegate = delegate;
77        }
78 
79        /**
80         * Check the history of this item, and if it has reached the retry limit,
81         * then return false.
82         * 
83         * @see org.springframework.batch.retry.RetryPolicy#canRetry(org.springframework.batch.retry.RetryContext)
84         */
85        public boolean canRetry(RetryContext context) {
86                return ((RetryPolicy) context).canRetry(context);
87        }
88 
89        /**
90         * Delegates to the delegate context.
91         * 
92         * @see org.springframework.batch.retry.RetryPolicy#close(org.springframework.batch.retry.RetryContext)
93         */
94        public void close(RetryContext context) {
95                ((RetryPolicy) context).close(context);
96        }
97 
98        /**
99         * Create a new context for the execution of the callback, which must be an
100         * instance of {@link ItemWriterRetryCallback}.
101         * 
102         * @see org.springframework.batch.retry.RetryPolicy#open(org.springframework.batch.retry.RetryCallback,
103         * RetryContext)
104         * 
105         * @throws IllegalStateException if the callback is not of the required
106         * type.
107         */
108        public RetryContext open(RetryCallback callback, RetryContext parent) {
109                Assert.state(callback instanceof ItemWriterRetryCallback, "Callback must be ItemProviderRetryCallback");
110                ItemWriterRetryContext context = new ItemWriterRetryContext((ItemWriterRetryCallback) callback, parent);
111                context.open(callback, null);
112                return context;
113        }
114 
115        /**
116         * If {@link #canRetry(RetryContext)} is false then take remedial action (if
117         * implemented by subclasses), and remove the current item from the history.
118         * 
119         * @see org.springframework.batch.retry.RetryPolicy#registerThrowable(org.springframework.batch.retry.RetryContext,
120         * java.lang.Throwable)
121         */
122        public void registerThrowable(RetryContext context, Throwable throwable) throws TerminatedRetryException {
123                ((RetryPolicy) context).registerThrowable(context, throwable);
124                // The throwable is stored in the delegate context.
125        }
126 
127        /**
128         * Call recovery path (if any) and clean up context history.
129         * 
130         * @see org.springframework.batch.retry.policy.AbstractStatefulRetryPolicy#handleRetryExhausted(org.springframework.batch.retry.RetryContext)
131         */
132        public Object handleRetryExhausted(RetryContext context) throws Exception {
133                return ((RetryPolicy) context).handleRetryExhausted(context);
134        }
135 
136        private class ItemWriterRetryContext extends RetryContextSupport implements RetryPolicy {
137 
138                final private Object item;
139 
140                final private Object key;
141 
142                final private int initialHashCode;
143 
144                // The delegate context...
145                private RetryContext delegateContext;
146 
147                final private ItemRecoverer recoverer;
148 
149                final private ItemKeyGenerator keyGenerator;
150 
151                final private FailedItemIdentifier failedItemIdentifier;
152 
153                public ItemWriterRetryContext(ItemWriterRetryCallback callback, RetryContext parent) {
154                        super(parent);
155                        this.recoverer = callback.getRecoverer();
156                        this.keyGenerator = callback.getKeyGenerator();
157                        this.item = callback.getItem();
158                        this.key = keyGenerator.getKey(item);
159                        this.failedItemIdentifier = callback.getFailedItemIdentifier();
160                        this.initialHashCode = key.hashCode();
161                }
162 
163                public boolean canRetry(RetryContext context) {
164                        return delegate.canRetry(this.delegateContext);
165                }
166 
167                public void close(RetryContext context) {
168                        delegate.close(this.delegateContext);
169                }
170 
171                public RetryContext open(RetryCallback callback, RetryContext parent) {
172                        if (hasFailed(failedItemIdentifier, key)) {
173                                this.delegateContext = retryContextCache.get(key);
174                                if (this.delegateContext == null) {
175                                        throw new RetryException("Inconsistent state for failed item: no history found. "
176                                                        + "Consider whether equals() or hashCode() for the item might be inconsistent, "
177                                                        + "or if you need to supply a better ItemKeyGenerator");
178                                }
179                        }
180                        else {
181                                // Only create a new context if we don't know the history of
182                                // this item:
183                                this.delegateContext = delegate.open(callback, null);
184                        }
185                        // The return value shouldn't be used...
186                        return null;
187                }
188 
189                public void registerThrowable(RetryContext context, Throwable throwable) throws TerminatedRetryException {
190                        // TODO: this comparison assumes that hashCode is the limiting
191                        // factor. Actually the cache should be able to decide for us.
192                        if (this.initialHashCode != key.hashCode()) {
193                                throw new RetryException("Inconsistent state for failed item key: hashCode has changed. "
194                                                + "Consider whether equals() or hashCode() for the item might be inconsistent, "
195                                                + "or if you need to supply a better ItemKeyGenerator");
196                        }
197                        retryContextCache.put(key, this.delegateContext);
198                        delegate.registerThrowable(this.delegateContext, throwable);
199                }
200 
201                public boolean isExternal() {
202                        // Not called...
203                        throw new UnsupportedOperationException("Not supported - this code should be unreachable.");
204                }
205 
206                public boolean shouldRethrow(RetryContext context) {
207                        // Not called...
208                        throw new UnsupportedOperationException("Not supported - this code should be unreachable.");
209                }
210 
211                public Object handleRetryExhausted(RetryContext context) throws Exception {
212                        // If there is no going back, then we can remove the history
213                        retryContextCache.remove(key);
214                        RepeatSynchronizationManager.setCompleteOnly();
215                        if (recoverer != null) {
216                                boolean success = recoverer.recover(item, context.getLastThrowable());
217                                if (!success) {
218                                        int count = context.getRetryCount();
219                                        logger.error("Could not recover from error after retry exhausted after [" + count + "] attempts.",
220                                                        context.getLastThrowable());
221                                }
222                        }
223                        return item;
224                }
225 
226                public Throwable getLastThrowable() {
227                        return delegateContext.getLastThrowable();
228                }
229 
230                public int getRetryCount() {
231                        return delegateContext.getRetryCount();
232                }
233 
234        }
235 
236        /**
237         * Extension point for cases where it is possible to avoid a cache hit by
238         * inspecting the item to determine if could ever have been seen before. In
239         * a messaging environment where the item is a message, it can be inspected
240         * to see if it has been delivered before.<br/>
241         * 
242         * The default implementation of this method checks for a non-null
243         * {@link FailedItemIdentifier}. Otherwise we just check the cache for the
244         * item key.
245         * 
246         * @param failedItemIdentifier
247         * @param key
248         */
249        protected boolean hasFailed(FailedItemIdentifier failedItemIdentifier, Object key) {
250                if (failedItemIdentifier != null) {
251                        return failedItemIdentifier.hasFailed(key);
252                }
253                return retryContextCache.containsKey(key);
254        }
255}

[all classes][org.springframework.batch.retry.policy]
EMMA 2.0.5312 (C) Vladimir Roubtsov