BatchQueue.java

package com.designpatterns.behavioral.command.applied;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Each unit of work is queued as an object instead of executed immediately, which is what
 * makes replay possible: a failed command is the exact same object, re-queued, not a
 * re-derived request. Without Command, "retry this specific unit of work" would need to
 * reconstruct the original request from scratch every time.
 */
public class BatchQueue {

    private static final int MAX_ATTEMPTS = 3;

    private final Deque<RecordProcessingCommand> pending = new ArrayDeque<>();
    private final Map<String, Integer> attempts = new HashMap<>();
    private final List<String> succeeded = new ArrayList<>();
    private final List<String> failed = new ArrayList<>();

    public void submit(RecordProcessingCommand command) {
        pending.add(command);
    }

    public void runAll() {
        while (!pending.isEmpty()) {
            RecordProcessingCommand command = pending.poll();
            int attempt = attempts.merge(command.recordId(), 1, Integer::sum);
            try {
                command.execute();
                succeeded.add(command.recordId());
            } catch (RuntimeException e) {
                if (attempt < MAX_ATTEMPTS) {
                    pending.add(command);
                } else {
                    failed.add(command.recordId());
                }
            }
        }
    }

    public List<String> succeeded() {
        return List.copyOf(succeeded);
    }

    public List<String> failed() {
        return List.copyOf(failed);
    }
}