/* * Copyright 2012 C24 Technologies. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package biz.c24.io.spring.batch.processor; import java.util.Collection; import java.util.LinkedList; import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemProcessor; import org.springframework.batch.item.ItemStream; import org.springframework.batch.item.ItemStreamException; import biz.c24.io.api.data.ComplexDataObject; import biz.c24.io.api.data.ValidationEvent; import biz.c24.io.api.data.ValidationException; import biz.c24.io.api.data.ValidationListener; import biz.c24.io.api.data.ValidationManager; import biz.c24.io.spring.batch.C24CompoundValidationException; import biz.c24.io.spring.batch.reader.C24ValidationException; /** * An ItemProcessor that validates a ComplexDataObject. * While no changes are made to the ComplexDataObject, it does allow an ItemProcessorListener to be * wired in to catch any ComplexDataObjects which fail validation. * * @author AndrewElmore * */ public class C24ValidatingItemProcessor implements ItemProcessor<ComplexDataObject, ComplexDataObject>, ItemStream { private ThreadLocal<ValidationManager> validators = null; /** * Whether or not to abort on the first failure */ private boolean failfast = true; /* * (non-Javadoc) * @see org.springframework.batch.item.ItemProcessor#process(java.lang.Object) */ @Override public ComplexDataObject process(ComplexDataObject item) throws Exception { ValidationManager mgr = validators.get(); if(mgr == null) { mgr = new ValidationManager(); validators.set(mgr); } try { if(failfast) { mgr.validateByException(item); } else { // Capture all failures final Collection<ValidationEvent> events = new LinkedList<ValidationEvent>(); ValidationListener listener = new ValidationListener() { public void validationPassed(ValidationEvent ve) { } public void validationFailed(ValidationEvent ve) { events.add(ve); } }; mgr.addValidationListener(listener); try { if(!mgr.validateByEvents(item)) { if(events.size() == 1) { // Treat it as though we were validating by exception mgr.setEventBased(false); mgr.fireValidationEvent(events.iterator().next()); } else { throw new C24CompoundValidationException(item, events); } } } finally { mgr.removeValidationListener(listener); } } } catch(ValidationException vEx) { throw new C24ValidationException("Failed to validate message: " + vEx.getLocalizedMessage(), item, vEx); } return item; } @Override public void open(ExecutionContext executionContext) throws ItemStreamException { validators = new ThreadLocal<ValidationManager>(); } @Override public void update(ExecutionContext executionContext) throws ItemStreamException { } @Override public void close() throws ItemStreamException { validators = null; } }