diff --git a/template_java/.gitignore b/template_java/.gitignore
new file mode 100644
index 0000000..c35584b
--- /dev/null
+++ b/template_java/.gitignore
@@ -0,0 +1,32 @@
+# Created by https://www.toptal.com/developers/gitignore/api/java
+# Edit at https://www.toptal.com/developers/gitignore?templates=java
+
+target/
+bin/da_proc.jar
+
+### Java ###
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+# End of https://www.toptal.com/developers/gitignore/api/java
diff --git a/template_java/bin/README b/template_java/bin/README
new file mode 100644
index 0000000..ced5c3b
--- /dev/null
+++ b/template_java/bin/README
@@ -0,0 +1 @@
+This is a reserved directory name! Store the binary generated by `build.sh` in this directory
diff --git a/template_java/bin/deploy/README b/template_java/bin/deploy/README
new file mode 100644
index 0000000..1a2f94d
--- /dev/null
+++ b/template_java/bin/deploy/README
@@ -0,0 +1 @@
+This is a reserved directory name, do not delete or use in your application!
diff --git a/template_java/bin/logs/README b/template_java/bin/logs/README
new file mode 100644
index 0000000..1a2f94d
--- /dev/null
+++ b/template_java/bin/logs/README
@@ -0,0 +1 @@
+This is a reserved directory name, do not delete or use in your application!
diff --git a/template_java/build.sh b/template_java/build.sh
new file mode 100755
index 0000000..28a73eb
--- /dev/null
+++ b/template_java/build.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -e
+
+# Change the current working directory to the location of the present file
+cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+
+mvn clean compile assembly:single
+mv target/da_project-1.0-SNAPSHOT-jar-with-dependencies.jar bin/da_proc.jar
diff --git a/template_java/cleanup.sh b/template_java/cleanup.sh
new file mode 100755
index 0000000..51b3a48
--- /dev/null
+++ b/template_java/cleanup.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# Change the current working directory to the location of the present file
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+
+rm -f "$DIR"/bin/da_proc.jar
+rm -rf "$DIR"/target
diff --git a/template_java/pom.xml b/template_java/pom.xml
new file mode 100644
index 0000000..ca664cd
--- /dev/null
+++ b/template_java/pom.xml
@@ -0,0 +1,64 @@
+
+
+
+ 4.0.0
+
+ cs451
+ da_project
+ 1.0-SNAPSHOT
+
+ DA_Project
+
+ http://www.example.com
+
+
+ UTF-8
+ 1.11
+ 1.11
+
+
+
+
+
+
+
+ maven-clean-plugin
+ 3.1.0
+
+ 11
+
+
+
+
+ maven-compiler-plugin
+ 3.8.1
+
+ 11
+
+
+
+ maven-jar-plugin
+ 3.2.0
+
+ 11
+
+
+
+ maven-assembly-plugin
+
+
+
+ cs451.Main
+
+
+
+ jar-with-dependencies
+
+
+
+
+
+
+
+
diff --git a/template_java/run.sh b/template_java/run.sh
new file mode 100755
index 0000000..4f300c7
--- /dev/null
+++ b/template_java/run.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# Change the current working directory to the location of the present file
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+
+ret=0
+exec 3>&1; $(java -jar "$DIR"/bin/da_proc.jar "$@" >&3); ret=$?; exec 3>&-
+
+exit $ret
diff --git a/template_java/src/main/java/cs451/BarrierParser.java b/template_java/src/main/java/cs451/BarrierParser.java
new file mode 100644
index 0000000..9f6245c
--- /dev/null
+++ b/template_java/src/main/java/cs451/BarrierParser.java
@@ -0,0 +1,76 @@
+package cs451;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+public class BarrierParser {
+
+ private static final String BARRIER_KEY = "--barrier";
+
+ private static final int BARRIER_ARGS_NUM = 2;
+ private static final String COLON_REGEX = ":";
+ private static final String IP_START_REGEX = "/";
+
+ private static String ip;
+ private static int port;
+
+ public boolean populate(String key, String value) {
+ if (!key.equals(BARRIER_KEY)) {
+ return false;
+ }
+
+ String[] barrier = value.split(COLON_REGEX);
+ if (barrier.length != BARRIER_ARGS_NUM) {
+ return false;
+ }
+
+ try {
+ String ipTest = InetAddress.getByName(barrier[0]).toString();
+ if (ipTest.startsWith(IP_START_REGEX)) {
+ ip = ipTest.substring(1);
+ } else {
+ ip = InetAddress.getByName(ipTest.split(IP_START_REGEX)[0]).getHostAddress();
+ }
+
+ port = Integer.parseInt(barrier[1]);
+ if (port <= 0) {
+ System.err.println("Barrier port must be a positive number!");
+ return false;
+ }
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ }
+
+ return true;
+ }
+
+ public String getIp() {
+ return ip;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public static class Barrier {
+
+ public static void waitOnBarrier() {
+ try (Socket socket = new Socket(ip, port)) {
+ InputStream input = socket.getInputStream();
+ InputStreamReader reader = new InputStreamReader(input);
+ System.out.println("Accessing barrier...");
+
+ int character;
+ while ((character = reader.read()) != -1) {}
+ } catch (IOException ex) {
+ System.out.println("I/O error: " + ex.getMessage());
+ }
+ }
+
+ }
+
+}
diff --git a/template_java/src/main/java/cs451/ConfigParser.java b/template_java/src/main/java/cs451/ConfigParser.java
new file mode 100644
index 0000000..0f97051
--- /dev/null
+++ b/template_java/src/main/java/cs451/ConfigParser.java
@@ -0,0 +1,19 @@
+package cs451;
+
+import java.io.File;
+
+public class ConfigParser {
+
+ private String path;
+
+ public boolean populate(String value) {
+ File file = new File(value);
+ path = file.getPath();
+ return true;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+}
diff --git a/template_java/src/main/java/cs451/Constants.java b/template_java/src/main/java/cs451/Constants.java
new file mode 100644
index 0000000..bbba87d
--- /dev/null
+++ b/template_java/src/main/java/cs451/Constants.java
@@ -0,0 +1,26 @@
+package cs451;
+
+public class Constants {
+
+ public static final int ARG_LIMIT_NO_CONFIG = 8;
+ public static final int ARG_LIMIT_CONFIG = 9;
+
+ // indexes for id
+ public static final int ID_KEY = 0;
+ public static final int ID_VALUE = 1;
+
+ // indexes for hosts
+ public static final int HOSTS_KEY = 2;
+ public static final int HOSTS_VALUE = 3;
+
+ // indexes for barrier
+ public static final int BARRIER_KEY = 4;
+ public static final int BARRIER_VALUE = 5;
+
+ // indexes for output
+ public static final int OUTPUT_KEY = 6;
+ public static final int OUTPUT_VALUE = 7;
+
+ // indexes for config
+ public static final int CONFIG_VALUE = 8;
+}
diff --git a/template_java/src/main/java/cs451/Host.java b/template_java/src/main/java/cs451/Host.java
new file mode 100644
index 0000000..3c2e360
--- /dev/null
+++ b/template_java/src/main/java/cs451/Host.java
@@ -0,0 +1,56 @@
+package cs451;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+public class Host {
+
+ private static final String IP_START_REGEX = "/";
+
+ private int id;
+ private String ip;
+ private int port = -1;
+
+ public boolean populate(String idString, String ipString, String portString) {
+ try {
+ id = Integer.parseInt(idString);
+
+ String ipTest = InetAddress.getByName(ipString).toString();
+ if (ipTest.startsWith(IP_START_REGEX)) {
+ ip = ipTest.substring(1);
+ } else {
+ ip = InetAddress.getByName(ipTest.split(IP_START_REGEX)[0]).getHostAddress();
+ }
+
+ port = Integer.parseInt(portString);
+ if (port <= 0) {
+ System.err.println("Port in the hosts file must be a positive number!");
+ return false;
+ }
+ } catch (NumberFormatException e) {
+ if (port == -1) {
+ System.err.println("Id in the hosts file must be a number!");
+ } else {
+ System.err.println("Port in the hosts file must be a number!");
+ }
+ return false;
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ }
+
+ return true;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getIp() {
+ return ip;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+}
diff --git a/template_java/src/main/java/cs451/HostsParser.java b/template_java/src/main/java/cs451/HostsParser.java
new file mode 100644
index 0000000..d0ebfc2
--- /dev/null
+++ b/template_java/src/main/java/cs451/HostsParser.java
@@ -0,0 +1,88 @@
+package cs451;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class HostsParser {
+
+ private static final String HOSTS_KEY = "--hosts";
+ private static final String SPACES_REGEX = "\\s+";
+
+ private String filename;
+ private List hosts = new ArrayList<>();
+
+ public boolean populate(String key, String filename) {
+ if (!key.equals(HOSTS_KEY)) {
+ return false;
+ }
+
+ this.filename = filename;
+ try(BufferedReader br = new BufferedReader(new FileReader(filename))) {
+ int lineNum = 1;
+ for(String line; (line = br.readLine()) != null; lineNum++) {
+ if (line.isBlank()) {
+ continue;
+ }
+
+ String[] splits = line.split(SPACES_REGEX);
+ if (splits.length != 3) {
+ System.err.println("Problem with the line " + lineNum + " in the hosts file!");
+ return false;
+ }
+
+ Host newHost = new Host();
+ if (!newHost.populate(splits[0], splits[1], splits[2])) {
+ return false;
+ }
+
+ hosts.add(newHost);
+ }
+ } catch (IOException e) {
+ System.err.println("Problem with the hosts file!");
+ return false;
+ }
+
+ if (!checkIdRange()) {
+ System.err.println("Hosts ids are not within the range!");
+ return false;
+ }
+
+ // sort by id
+ Collections.sort(hosts, new HostsComparator());
+ return true;
+ }
+
+ private boolean checkIdRange() {
+ int num = hosts.size();
+ for (Host host : hosts) {
+ if (host.getId() < 1 || host.getId() > num) {
+ System.err.println("Id of a host is not in the right range!");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public boolean inRange(int id) {
+ return id < hosts.size();
+ }
+
+ public List getHosts() {
+ return hosts;
+ }
+
+ class HostsComparator implements Comparator {
+
+ public int compare(Host a, Host b) {
+ return a.getId() - b.getId();
+ }
+
+ }
+
+}
diff --git a/template_java/src/main/java/cs451/IdParser.java b/template_java/src/main/java/cs451/IdParser.java
new file mode 100644
index 0000000..c40260a
--- /dev/null
+++ b/template_java/src/main/java/cs451/IdParser.java
@@ -0,0 +1,31 @@
+package cs451;
+
+public class IdParser {
+
+ private static final String ID_KEY = "--id";
+
+ private int id;
+
+ public boolean populate(String key, String value) {
+ if (!key.equals(ID_KEY)) {
+ return false;
+ }
+
+ try {
+ id = Integer.parseInt(value);
+ if (id <= 0) {
+ System.err.println("Id must be a positive number!");
+ }
+ } catch (NumberFormatException e) {
+ System.err.println("Id must be a number!");
+ return false;
+ }
+
+ return true;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+}
diff --git a/template_java/src/main/java/cs451/Main.java b/template_java/src/main/java/cs451/Main.java
new file mode 100644
index 0000000..394c8fa
--- /dev/null
+++ b/template_java/src/main/java/cs451/Main.java
@@ -0,0 +1,53 @@
+package cs451;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.Socket;
+
+public class Main {
+
+ private static void handleSignal() {
+ //immediately stop network packet processing
+ System.out.println("Immediately stopping network packet processing.");
+
+ //write/flush output file if necessary
+ System.out.println("Writing output.");
+ }
+
+ private static void initSignalHandlers() {
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ handleSignal();
+ }
+ });
+ }
+
+ public static void main(String[] args) {
+ Parser parser = new Parser(args);
+ parser.parse();
+
+ initSignalHandlers();
+
+ // example
+ long pid = ProcessHandle.current().pid();
+ System.out.println("My PID is " + pid + ".");
+ System.out.println("Use 'kill -SIGINT " + pid + " ' or 'kill -SIGTERM " + pid + " ' to stop processing packets.");
+
+ System.out.println("My id is " + parser.myId() + ".");
+ System.out.println("List of hosts is:");
+ for (Host host: parser.hosts()) {
+ System.out.println(host.getId() + ", " + host.getIp() + ", " + host.getPort());
+ }
+
+ System.out.println("Barrier: " + parser.barrierIp() + ":" + parser.barrierPort());
+ System.out.println("Output: " + parser.output());
+ // if config is defined; always check before parser.config()
+ if (parser.hasConfig()) {
+ System.out.println("Config: " + parser.config());
+ }
+
+ BarrierParser.Barrier.waitOnBarrier();
+ }
+}
diff --git a/template_java/src/main/java/cs451/OutputParser.java b/template_java/src/main/java/cs451/OutputParser.java
new file mode 100644
index 0000000..b1a6adc
--- /dev/null
+++ b/template_java/src/main/java/cs451/OutputParser.java
@@ -0,0 +1,25 @@
+package cs451;
+
+import java.io.File;
+
+public class OutputParser {
+
+ private static final String OUTPUT_KEY = "--output";
+
+ private String path;
+
+ public boolean populate(String key, String value) {
+ if (!key.equals(OUTPUT_KEY)) {
+ return false;
+ }
+
+ File file = new File(value);
+ path = file.getPath();
+ return true;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+}
diff --git a/template_java/src/main/java/cs451/Parser.java b/template_java/src/main/java/cs451/Parser.java
new file mode 100644
index 0000000..cff51cf
--- /dev/null
+++ b/template_java/src/main/java/cs451/Parser.java
@@ -0,0 +1,93 @@
+package cs451;
+
+import java.util.List;
+
+public class Parser {
+
+ private String[] args;
+ private long pid;
+ private IdParser idParser;
+ private HostsParser hostsParser;
+ private BarrierParser barrierParser;
+ private OutputParser outputParser;
+ private ConfigParser configParser;
+
+ public Parser(String[] args) {
+ this.args = args;
+ }
+
+ public void parse() {
+ pid = ProcessHandle.current().pid();
+
+ idParser = new IdParser();
+ hostsParser = new HostsParser();
+ barrierParser = new BarrierParser();
+ outputParser = new OutputParser();
+ configParser = null;
+
+ int argsNum = args.length;
+ if (argsNum != Constants.ARG_LIMIT_NO_CONFIG && argsNum != Constants.ARG_LIMIT_CONFIG) {
+ help();
+ }
+
+ if (!idParser.populate(args[Constants.ID_KEY], args[Constants.ID_VALUE])) {
+ help();
+ }
+
+ if (!hostsParser.populate(args[Constants.HOSTS_KEY], args[Constants.HOSTS_VALUE])) {
+ help();
+ }
+
+ if (!hostsParser.inRange(idParser.getId())) {
+ help();
+ }
+
+ if (!barrierParser.populate(args[Constants.BARRIER_KEY], args[Constants.BARRIER_VALUE])) {
+ help();
+ }
+
+ if (!outputParser.populate(args[Constants.OUTPUT_KEY], args[Constants.OUTPUT_VALUE])) {
+ help();
+ }
+
+ if (argsNum == Constants.ARG_LIMIT_CONFIG) {
+ configParser = new ConfigParser();
+ if (!configParser.populate(args[Constants.CONFIG_VALUE])) {
+ }
+ }
+ }
+
+ private void help() {
+ System.err.println("Usage: --id ID --hosts HOSTS --barier NAME:PORT --output OUTPUT [config]");
+ System.exit(1);
+ }
+
+ public int myId() {
+ return idParser.getId();
+ }
+
+ public List hosts() {
+ return hostsParser.getHosts();
+ }
+
+ public String barrierIp() {
+ return barrierParser.getIp();
+ }
+
+ public int barrierPort() {
+ return barrierParser.getPort();
+ }
+
+ public String output() {
+ return outputParser.getPath();
+ }
+
+ public boolean hasConfig() {
+ return configParser != null;
+ }
+
+ public String config() {
+ return configParser.getPath();
+ }
+
+}