001/* 002 * Copyright 2006-2013 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 */ 016package org.springframework.batch.core.step.skip; 017 018import java.io.FileNotFoundException; 019import java.util.Collections; 020import java.util.Map; 021 022import org.springframework.batch.core.Step; 023import org.springframework.batch.core.StepExecution; 024import org.springframework.batch.item.file.FlatFileParseException; 025import org.springframework.classify.BinaryExceptionClassifier; 026import org.springframework.classify.Classifier; 027 028/** 029 * <p> 030 * {@link SkipPolicy} that determines whether or not reading should continue 031 * based upon how many items have been skipped. This is extremely useful 032 * behavior, as it allows you to skip records, but will throw a 033 * {@link SkipLimitExceededException} if a set limit has been exceeded. For 034 * example, it is generally advisable to skip {@link FlatFileParseException}s, 035 * however, if the vast majority of records are causing exceptions, the file is 036 * likely bad. 037 * </p> 038 * 039 * <p> 040 * Furthermore, it is also likely that you only want to skip certain exceptions. 041 * {@link FlatFileParseException} is a good example of an exception you will 042 * likely want to skip, but a {@link FileNotFoundException} should cause 043 * immediate termination of the {@link Step}. A {@link Classifier} is used to 044 * determine whether a particular exception is skippable or not. 045 * </p> 046 * 047 * @author Ben Hale 048 * @author Lucas Ward 049 * @author Robert Kasanicky 050 * @author Dave Syer 051 * @author Dan Garrette 052 */ 053public class LimitCheckingItemSkipPolicy implements SkipPolicy { 054 055 private int skipLimit; 056 057 private Classifier<Throwable, Boolean> skippableExceptionClassifier; 058 059 /** 060 * Convenience constructor that assumes all exception types are fatal. 061 */ 062 public LimitCheckingItemSkipPolicy() { 063 this(0, Collections.<Class<? extends Throwable>, Boolean> emptyMap()); 064 } 065 066 /** 067 * @param skipLimit the number of skippable exceptions that are allowed to 068 * be skipped 069 * @param skippableExceptions exception classes that can be skipped 070 * (non-critical) 071 */ 072 public LimitCheckingItemSkipPolicy(int skipLimit, Map<Class<? extends Throwable>, Boolean> skippableExceptions) { 073 this(skipLimit, new BinaryExceptionClassifier(skippableExceptions)); 074 } 075 076 /** 077 * @param skipLimit the number of skippable exceptions that are allowed to 078 * be skipped 079 * @param skippableExceptionClassifier exception classifier for those that 080 * can be skipped (non-critical) 081 */ 082 public LimitCheckingItemSkipPolicy(int skipLimit, Classifier<Throwable, Boolean> skippableExceptionClassifier) { 083 this.skipLimit = skipLimit; 084 this.skippableExceptionClassifier = skippableExceptionClassifier; 085 } 086 087 /** 088 * The absolute number of skips (of skippable exceptions) that can be 089 * tolerated before a failure. 090 * 091 * @param skipLimit the skip limit to set 092 */ 093 public void setSkipLimit(int skipLimit) { 094 this.skipLimit = skipLimit; 095 } 096 097 /** 098 * The classifier that will be used to decide on skippability. If an 099 * 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 @Override 127 public boolean shouldSkip(Throwable t, int skipCount) { 128 if (skippableExceptionClassifier.classify(t)) { 129 if (skipCount < skipLimit) { 130 return true; 131 } 132 else { 133 throw new SkipLimitExceededException(skipLimit, t); 134 } 135 } 136 else { 137 return false; 138 } 139 } 140 141}