docs: private Object readItem() {
  
          Object itemRead = null;
  
          try {
  
              currentChunkStatus.incrementItemsTouchedInCurrentChunk();
  
              // call read listeners before and after the actual read
              for (ItemReadListenerProxy readListenerProxy : itemReadListeners) {
                  readListenerProxy.beforeRead();
              }
  
              itemRead = readerProxy.readItem();
  
              for (ItemReadListenerProxy readListenerProxy : itemReadListeners) {
                  readListenerProxy.afterRead(itemRead);
              }
  
              // itemRead == null means we reached the end of
              // the readerProxy "resultset"
              if (itemRead == null) {
                  currentChunkStatus.markReadNull();
                  currentChunkStatus.decrementItemsTouchedInCurrentChunk();
              }
  
          } catch (Exception e) {
              runtimeStepExecution.setException(e);
              for (ItemReadListenerProxy readListenerProxy : itemReadListeners) {
                  readListenerProxy.onReadError(e);
              }
              if (!currentChunkStatus.isRetryingAfterRollback()) {
                  if (retryReadException(e)) {
                      if (!retryHandler.isRollbackException(e)) {
                          // retry without rollback
                          itemRead = readItem();
                      } else {
                          // retry with rollback
                          currentChunkStatus.markForRollbackWithRetry(e);
                      }
                  } else if (skipReadException(e)) {
                      currentItemStatus.setSkipped(true);
                      runtimeStepExecution.getMetric(MetricImpl.MetricType.READ_SKIP_COUNT).incValue();
                  } else {
                      throw new BatchContainerRuntimeException(e);
                  }
              } else {
                  // coming from a rollback retry
                  if (skipReadException(e)) {
                      currentItemStatus.setSkipped(true);
                      runtimeStepExecution.getMetric(MetricImpl.MetricType.READ_SKIP_COUNT).incValue();
  
                  } else if (retryReadException(e)) {
                      if (!retryHandler.isRollbackException(e)) {
                          // retry without rollback
                          itemRead = readItem();
                      } else {
                          // retry with rollback
                          currentChunkStatus.markForRollbackWithRetry(e);
                      }
                  } else {
                      throw new BatchContainerRuntimeException(e);
                  }
              }
  
          } catch (Throwable e) {
              throw new BatchContainerRuntimeException(e);
          }
  
          logger.exiting(sourceClass, "readItem", itemRead == null ? "<null>" : itemRead);
          return itemRead;
      }