import java.net.*;
import java.io.*;

public class SureDelivery implements Alarmable {
  protected DatagramSocket socket;
  protected DatagramPacket packet;
  protected Alarm alarm;
  
  public SureDelivery (String message, String host, int port)
      throws IOException {
    socket = new DatagramSocket ();
    buildPacket (message, host, port);
    try {
      sendPacket ();
      receivePacket ();
    } finally {
      alarm.stop ();
      socket.close ();
    }
  }

  protected void buildPacket (String message, String host, int port) throws
      IOException {
    ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream ();
    DataOutputStream dataOut = new DataOutputStream (byteArrayOut);
    dataOut.writeUTF (message);
    byte[] data = byteArrayOut.toByteArray ();
    packet = new DatagramPacket (data, data.length, InetAddress.getByName
      (host), port);
  }

  protected void sendPacket () throws IOException {
    socket.send (packet);
    System.out.println ("Sent packet.");
    alarm = new Alarm (10000, this);
    alarm.start ();
  }

  protected boolean received;
  protected void receivePacket () throws IOException {
    byte buffer[] = new byte[65508];
    DatagramPacket packet = new DatagramPacket (buffer, buffer.length);
    socket.receive (packet);
    received = true;
    ByteArrayInputStream byteArrayIn =
      new ByteArrayInputStream (packet.getData (), 0, packet.getLength ());
    DataInputStream dataIn = new DataInputStream (byteArrayIn);
    String result = dataIn.readUTF ();
    System.out.println ("Received " + result + ".");
  }

  public synchronized void alarmCall (Object object) {
    try {
      System.out.println ("Alarm!");
      if (!received)
        sendPacket ();
    } catch (IOException ex) {
      ex.printStackTrace ();
    }
  }

  public static void main (String[] args) throws InterruptedException,
      IOException {
    if (args.length != 3)
    throw new IllegalArgumentException
      ("Syntax: SureDelivery <host> <port> <message>");
    while (true) {
      new SureDelivery (args[2], args[0], Integer.parseInt (args[1]));
      System.out.println ("Pause...");
      Thread.sleep (2000);
    }
  }
}

class Alarm implements Runnable {
  
  public Alarm (int time, Alarmable target) {
    this (time, target, null);
  }
  protected Alarmable target;
  protected Object arg;
  protected int time;
  
  public Alarm (int time, Alarmable target, Object arg) {
    this.time = time;
    this.target = target;
    this.arg = arg;
  }

  protected Thread alarm;

  public synchronized void start () {
    if (alarm == null) {
      alarm = new Thread (this);
      alarm.start (); 
    }
  }

  public synchronized void stop () {
    if (alarm != null) {
      alarm.interrupt ();
      alarm = null;
    }
  }

  public void run () {
    try {
      Thread.sleep (time);
      synchronized (this) {
        if (Thread.interrupted ())
          return;
        alarm = null;
      }
      target.alarmCall (arg);
    } catch (InterruptedException ignored) {
    }
  }
}

interface Alarmable {
  public void alarmCall (Object arg);
}