import java.net.InetAddress;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.NoSuchElementException;
import java.util.Scanner;

public class SendChannel implements LocalChannel {
  private static final String TAG = "sender thrd";
  private static final Logger log = new Logger(TAG);
  private static final String senderUXInstruction =
      "\tType messages & [enter] to send\n\t[enter] twice to exit.\n";

  private boolean isOk = true;
  private boolean stopped = false;

  private InetAddress destIP;
  private int destPort;
  private Scanner msgSrc;
  private DatagramSocket socket;
  private Thread running;
  public SendChannel(
      final Scanner src,
      final InetAddress destIP,
      final int destPort,
      DatagramSocket outSock) {
    this.msgSrc = src;
    this.destIP = destIP;
    this.destPort = destPort;
    this.socket = outSock;
  }

  public SendChannel setLogLevel(final Logger.Level lvl) {
    this.log.setLevel(lvl);
    return this;
  }

  public SendChannel start() {
    this.stopped = false;
    this.running = new Thread(this);
    this.running.setName(TAG);
    this.running.start();
    this.log.printf("spawned \"%s\" thread: %s\n", TAG, this.running);
    return this;
  }

  public SendChannel report() {
    this.log.printf(
        "READY to capture messages\n\tbound for %s on port %s\n\tvia socket: %s\n",
        this.destIP,
        this.destPort,
        this.socket.getLocalSocketAddress());
    return this;
  }

  public boolean isActive() { return !this.stopped; }
  public boolean isFailed() { return this.isOk; }
  public Thread thread() { return this.running; }

  public Thread stop() {
    this.stopped = true;
    return this.running;
  }

  public void run() {
    DatagramPacket packet;
    String message;

    this.log.printf("usage instructions:\n%s", senderUXInstruction);
    boolean isPrevEmpty = false;
    long msgIndex = 0;
    while (true) {
      if (this.stopped) {
        break;
      }

      try {
        message = this.msgSrc.nextLine().trim();
      } catch (NoSuchElementException e) {
        this.log.printf("caught EOF, exiting...\n");
        break;
      }

      if (message.length() == 0) {
        if (isPrevEmpty) {
          this.log.printf("caught two empty messages, exiting.... ");
          break;
        }
        isPrevEmpty = true;
        this.log.printf("press enter again to exit normally.\n");
        continue;
      }
      isPrevEmpty = false;
      msgIndex++;

      packet = new DatagramPacket(message.getBytes(), message.length(), destIP, destPort);

      this.log.printf("sending message #%03d: '%s'...", msgIndex, message);
      try {
        this.socket.send(packet);
        System.out.printf(" Done.\n");
      } catch (Exception e) {
        System.out.printf("\n");
        this.log.errorf(e, "\nfailed sending '%s'", message);
        this.isOk = false;
        break;
      }
    }

    this.msgSrc.close();
  }
}

@@ -152,16 +152,89 @@
 Scanner;
+%0Aimport java.net.URLEncoder;%0Aimport java.io.UnsupportedEncodingException;
 %0A%0Apublic
@@ -1793,24 +1793,175 @@
 nning;%0A  %7D%0A%0A
+  private void fatalf(Exception e, String format, Object... args) %7B%0A    this.log.errorf(e, format, args);%0A    this.stop();%0A    this.isOk = false;%0A  %7D%0A%0A
   public voi
@@ -2008,23 +2008,56 @@
  String 
-message
+rawMsg = null;%0A    String message = null
 ;%0A%0A    t
@@ -2265,23 +2265,22 @@
         
-message
+rawMsg
  = this.
@@ -2301,24 +2301,78 @@
 e().trim();%0A
+        message = URLEncoder.encode(rawMsg, %22UTF-8%22);%0A
       %7D catc
@@ -2469,32 +2469,165 @@
   break;%0A      %7D
+ catch (UnsupportedEncodingException e) %7B%0A        this.fatalf(e, %22failed encoding message %2503d '%25s'%22, rawMsg);%0A        break;%0A      %7D
 %0A%0A      if (mess
@@ -3262,33 +3262,29 @@
        this.
-log.error
+fatal
 f(e, %22%5Cnfail
@@ -3315,35 +3315,8 @@
 e);%0A
-        this.isOk = false;%0A
     

complete url encoding policy per 64a8e61ab9b58