import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

public class JadwalBerkala {

    public static void main(String[] args) throws Exception {
        ScheduledExecutorService penjadwal = Executors.newScheduledThreadPool(1);

        System.out.println("== job rentan: exception lolos ke luar ==");
        AtomicInteger hitung = new AtomicInteger();
        Queue<String> riwayat = new ConcurrentLinkedQueue<>();

        ScheduledFuture<?> rentan = penjadwal.scheduleAtFixedRate(() -> {
            int ke = hitung.incrementAndGet();
            if (ke == 3) {
                riwayat.add("GALAT");
                throw new RuntimeException("pelaporan gagal");
            }
            riwayat.add("OK");
        }, 0, 100, TimeUnit.MILLISECONDS);

        String laporan;
        try {
            rentan.get();   // menunggu sampai job gagal atau dibatalkan
            laporan = "selesai normal";
        } catch (ExecutionException e) {
            laporan = e.getClass().getSimpleName() + " -> " + e.getCause();
        }
        System.out.println("get() memberi    : " + laporan);

        // Setelah kegagalan, tidak ada eksekusi baru sama sekali.
        Thread.sleep(250);
        System.out.println("riwayat eksekusi : " + riwayat.stream().toList());
        System.out.println("total setelah 250 ms : " + hitung.get()
                + " -- job mati diam-diam");

        System.out.println();
        System.out.println("== job aman: exception ditangkap di dalam ==");
        AtomicInteger hitungAman = new AtomicInteger();
        Queue<String> riwayatAman = new ConcurrentLinkedQueue<>();
        AtomicReference<ScheduledFuture<?>> referensi = new AtomicReference<>();

        ScheduledFuture<?> aman = penjadwal.scheduleAtFixedRate(() -> {
            int ke = hitungAman.incrementAndGet();
            try {
                if (ke == 3) {
                    throw new RuntimeException("pelaporan gagal");
                }
                riwayatAman.add("OK");
            } catch (RuntimeException e) {
                // Job tetap hidup: galat dicatat, eksekusi berikutnya lanjut.
                riwayatAman.add("GALAT(" + e.getMessage() + ")");
            }
            if (ke >= 5) {
                referensi.get().cancel(false);   // berhenti terkontrol
            }
        }, 0, 100, TimeUnit.MILLISECONDS);
        referensi.set(aman);

        try {
            aman.get();
        } catch (CancellationException e) {
            System.out.println("get() memberi    : " + e.getClass().getSimpleName()
                    + " (pembatalan terkontrol)");
        }
        System.out.println("riwayat eksekusi : " + riwayatAman.stream().toList());
        System.out.println("total eksekusi   : " + hitungAman.get()
                + " -- job melewati kegagalan");

        penjadwal.shutdown();
    }
}
