import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.Semaphore;

public class PoolMini {

    public static void main(String[] args) {
        PoolKoneksi pool = new PoolKoneksi(3);

        KoneksiSimulasi c = null;
        try (KoneksiSimulasi a = pool.pinjam();
             KoneksiSimulasi b = pool.pinjam();
             KoneksiSimulasi c1 = pool.pinjam()) {
            c = c1; // simpan referensi untuk uji pemakaian ulang
            System.out.println("memegang koneksi : #" + a.nomor
                    + ", #" + b.nomor + ", #" + c1.nomor);

            // permintaan keempat saat seluruh koneksi sedang dipakai
            KoneksiSimulasi tolak = pool.pinjamBilaAda();
            System.out.println("pinjaman keempat : " + tolak);
        } // close otomatis: c1, b, a kembali ke pool dalam urutan kebalikan

        try (KoneksiSimulasi d = pool.pinjam()) {
            System.out.println("pinjaman baru    : koneksi #" + d.nomor
                    + ", objek sama dengan sebelumnya: " + (d == c));
        }

        System.out.println("koneksi dibuat   : " + pool.getDibuat());
        System.out.println("pinjaman dilayani: " + pool.getDilayani());
    }
}

/** Pool sederhana: Semaphore menjaga jumlah peminjaman maksimum. */
class PoolKoneksi {

    private final Deque<KoneksiSimulasi> bebas = new ArrayDeque<>();
    private final Semaphore izin;
    private int dibuat = 0;
    private int dilayani = 0;

    PoolKoneksi(int ukuran) {
        this.izin = new Semaphore(ukuran);
        for (int i = 1; i <= ukuran; i++) {
            bebas.add(new KoneksiSimulasi(this));
            dibuat++;
        }
        System.out.println("[pool] dibuat " + ukuran + " koneksi sejak awal");
    }

    /** Versi memblokir: menunggu sampai ada koneksi bebas. */
    KoneksiSimulasi pinjam() {
        izin.acquireUninterruptibly();
        KoneksiSimulasi k = bebas.poll();
        dilayani++;
        System.out.println("[pool] pinjam  koneksi #" + k.nomor
                + " (bebas: " + bebas.size() + ")");
        return k;
    }

    /** Versi tidak memblokir: null bila pool sedang habis. */
    KoneksiSimulasi pinjamBilaAda() {
        if (!izin.tryAcquire()) {
            System.out.println("[pool] DITOLAK: seluruh koneksi sedang dipakai");
            return null;
        }
        KoneksiSimulasi k = bebas.poll();
        dilayani++;
        System.out.println("[pool] pinjam  koneksi #" + k.nomor);
        return k;
    }

    void kembali(KoneksiSimulasi k) {
        bebas.add(k);
        izin.release();
        System.out.println("[pool] kembali koneksi #" + k.nomor
                + " (bebas: " + bebas.size() + ")");
    }

    int getDibuat() {
        return dibuat;
    }

    int getDilayani() {
        return dilayani;
    }
}

/** Koneksi simulasi: close() mengembalikan ke pool, bukan menutup fisik. */
class KoneksiSimulasi implements AutoCloseable {

    final int nomor;
    private final PoolKoneksi pool;
    private static int penomor = 0;

    KoneksiSimulasi(PoolKoneksi pool) {
        this.pool = pool;
        this.nomor = ++penomor;
    }

    @Override
    public void close() {
        pool.kembali(this);
    }
}
