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 | package org.springframework.batch.core.step.skip; |
17 | |
18 | import java.io.FileNotFoundException; |
19 | import java.util.Collections; |
20 | import java.util.Map; |
21 | |
22 | import org.springframework.batch.classify.BinaryExceptionClassifier; |
23 | import org.springframework.batch.classify.Classifier; |
24 | import org.springframework.batch.core.Step; |
25 | import org.springframework.batch.core.StepExecution; |
26 | import org.springframework.batch.item.file.FlatFileParseException; |
27 | |
28 | /** |
29 | * <p> |
30 | * {@link SkipPolicy} that determines whether or not reading should continue |
31 | * based upon how many items have been skipped. This is extremely useful |
32 | * behavior, as it allows you to skip records, but will throw a |
33 | * {@link SkipLimitExceededException} if a set limit has been exceeded. For |
34 | * example, it is generally advisable to skip {@link FlatFileParseException}s, |
35 | * however, if the vast majority of records are causing exceptions, the file is |
36 | * likely bad. |
37 | * </p> |
38 | * |
39 | * <p> |
40 | * Furthermore, it is also likely that you only want to skip certain exceptions. |
41 | * {@link FlatFileParseException} is a good example of an exception you will |
42 | * likely want to skip, but a {@link FileNotFoundException} should cause |
43 | * immediate termination of the {@link Step}. A {@link Classifier} is used to |
44 | * determine whether a particular exception is skippable or not. |
45 | * </p> |
46 | * |
47 | * @author Ben Hale |
48 | * @author Lucas Ward |
49 | * @author Robert Kasanicky |
50 | * @author Dave Syer |
51 | * @author Dan Garrette |
52 | */ |
53 | public class LimitCheckingItemSkipPolicy implements SkipPolicy { |
54 | |
55 | private int skipLimit; |
56 | |
57 | private Classifier<Throwable, Boolean> skippableExceptionClassifier; |
58 | |
59 | /** |
60 | * Convenience constructor that assumes all exception types are fatal. |
61 | */ |
62 | public LimitCheckingItemSkipPolicy() { |
63 | this(0, Collections.<Class<? extends Throwable>, Boolean> emptyMap()); |
64 | } |
65 | |
66 | /** |
67 | * @param skipLimit the number of skippable exceptions that are allowed to |
68 | * be skipped |
69 | * @param skippableExceptions exception classes that can be skipped |
70 | * (non-critical) |
71 | */ |
72 | public LimitCheckingItemSkipPolicy(int skipLimit, Map<Class<? extends Throwable>, Boolean> skippableExceptions) { |
73 | this(skipLimit, new BinaryExceptionClassifier(skippableExceptions)); |
74 | } |
75 | |
76 | /** |
77 | * @param skipLimit the number of skippable exceptions that are allowed to |
78 | * be skipped |
79 | * @param skippableExceptionClassifier exception classifier for those that |
80 | * can be skipped (non-critical) |
81 | */ |
82 | public LimitCheckingItemSkipPolicy(int skipLimit, Classifier<Throwable, Boolean> skippableExceptionClassifier) { |
83 | this.skipLimit = skipLimit; |
84 | this.skippableExceptionClassifier = skippableExceptionClassifier; |
85 | } |
86 | |
87 | /** |
88 | * The absolute number of skips (of skippable exceptions) that can be |
89 | * tolerated before a failure. |
90 | * |
91 | * @param skipLimit the skip limit to set |
92 | */ |
93 | public void setSkipLimit(int skipLimit) { |
94 | this.skipLimit = skipLimit; |
95 | } |
96 | |
97 | /** |
98 | * The classifier that will be used to decide on skippability. If an |
99 | * exception classifies as "true" then it is skippable, and otherwise not. |
100 | * |
101 | * @param skippableExceptionClassifier the skippableExceptionClassifier to |
102 | * set |
103 | */ |
104 | public void setSkippableExceptionClassifier(Classifier<Throwable, Boolean> skippableExceptionClassifier) { |
105 | this.skippableExceptionClassifier = skippableExceptionClassifier; |
106 | } |
107 | |
108 | /** |
109 | * Set up the classifier through a convenient map from throwable class to |
110 | * boolean (true if skippable). |
111 | * |
112 | * @param skippableExceptions the skippable exceptions to set |
113 | */ |
114 | public void setSkippableExceptionMap(Map<Class<? extends Throwable>, Boolean> skippableExceptions) { |
115 | this.skippableExceptionClassifier = new BinaryExceptionClassifier(skippableExceptions); |
116 | } |
117 | |
118 | /** |
119 | * Given the provided exception and skip count, determine whether or not |
120 | * processing should continue for the given exception. If the exception is |
121 | * not classified as skippable in the classifier, false will be returned. If |
122 | * the exception is classified as skippable and {@link StepExecution} |
123 | * skipCount is greater than the skipLimit, then a |
124 | * {@link SkipLimitExceededException} will be thrown. |
125 | */ |
126 | public boolean shouldSkip(Throwable t, int skipCount) { |
127 | if (skippableExceptionClassifier.classify(t)) { |
128 | if (skipCount < skipLimit) { |
129 | return true; |
130 | } |
131 | else { |
132 | throw new SkipLimitExceededException(skipLimit, t); |
133 | } |
134 | } |
135 | else { |
136 | return false; |
137 | } |
138 | } |
139 | |
140 | } |