import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.*;

public class App{
    public final static BlockingQueue<String> queue = new ArrayBlockingQueue<String>(20);
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService service = null;
        String threadName = Thread.currentThread().getName();
        try {
            service = Executors.newFixedThreadPool(6); // +1 thread for producer
            for(int i=0; i<20; ++i){queue.put("Hello");}
            service.submit(new Producer(service, queue)).get(); // Wait until producer exits
        } finally {
            if (null != service) {
                service.shutdown();
                System.out.printf("[%s] Awaiting termination...%n", threadName);
                try {
                    service.awaitTermination(1, TimeUnit.HOURS);
                } catch (InterruptedException e) {
                    System.err.println("Failed to terminate worker");
                }
            }
        }
        System.out.printf("[%s] Done%n", threadName);
    }
}

class Worker implements Runnable {
    private String message;

    public Worker(String message) {
        this.message = message;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        ThreadLocalRandom random = ThreadLocalRandom.current();
        try {
            System.out.printf("[%s] Sending message '%s'...%n", name, message);
            Thread.sleep(random.nextInt(50, 300));
            System.out.printf("[%s] Message '%s' successfully sent!%n", name, message);
        } catch (InterruptedException e) {
            System.err.printf("[%s] Received interrupt signal, exiting...%n", name);
        }
    }
}

class Producer implements Runnable {
    private final BlockingQueue<String> queue;
    private ExecutorService service;

    Producer(ExecutorService service, BlockingQueue<String> queue) {
        this.service = service;
        this.queue = queue;
    }

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.printf("[%s] Producer started. Enter \"exit\" to stop, or another string to " +
                "send it over message queue%n", threadName);
        while(!(Thread.currentThread().isInterrupted())){
            try{
                String message = queue.take();
                service.submit(new Worker(message));
            }catch (Exception e) {
                System.out.printf("[%s] IOException in producer, exiting...", threadName);
            }
        }
        System.out.printf("[%s] Producer shutdown", threadName);
    }
}
