public IBatchStatistics process(Target target) throws Exception {
		long started = System.currentTimeMillis();
		beforeProcessing(target);
		IBatchStatistics result = getResult(target);
		ProcessorsChain<ItemInput, IBatchStatistics, IProcessor> processor = getProcessorChain();
		Iterator<ItemInput> i = getIterator(target);
		logger.log(Level.FINE, "Processing started");
		cancelled = false;
		while (i.hasNext() && !cancelled) {

			ItemInput input = null;

			try {
				input = i.next();
				onItemRead(input, result);
				if (skip(input,result)) {
					onItemSkipped(input, result);
					continue;
				}
			} catch (Exception x) {
				onError(input, null, result, x);
				continue;
			}

			try {
				if (processor != null) {
					Object output = processor.process(input);
					try {
						onItemProcessed(input, output, result);
					} catch (ClassCastException x) {
						// weird class cast exception
						onItemProcessed(null, output, result);
					}
				}
			} catch (Exception x) {
				logger.log(Level.WARNING, x.getMessage(), x);
				onError(input, null, result, x);
				continue;
			}
			long elapsed = System.currentTimeMillis() - started;
			if ((timeout > 0) && (elapsed) > timeout) {
				onError(input,
						null,
						result,
						new AmbitException(String.format(
								"Aborted on timeout %d", elapsed)));
				break;
			}
		}

		afterProcessing(target, i);
		return result;
	}