// Path: core/src/main/java/me/gregorias/dfuntest/App.java
// public abstract class App<EnvironmentT extends Environment> {
//   private final int mId;
//   private final String mName;
// 
//   public App(int id, String name) {
//     mId = id;
//     mName = name;
//   }
// 
//   /**
//    * @return Underlying environment
//    */
//   public abstract EnvironmentT getEnvironment();
// 
//   /**
//    * @return Number identifying this application.
//    */
//   public int getId() {
//     return mId;
//   }
// 
//   /**
//    * @return Human readable name of this application.
//    */
//   public String getName() {
//     return mName;
//   }
// 
//   /**
//    * Starts the application and allows it to run in background.
//    */
//   @SuppressWarnings("unused")
//   public abstract void startUp() throws CommandException, IOException;
// 
//   /**
//    * Shuts down started application and deallocates all resources associated
//    * with running application.
//    */
//   @SuppressWarnings("unused")
//   public abstract void shutDown() throws IOException, InterruptedException;
// }
// 
// Path: core/src/main/java/me/gregorias/dfuntest/CommandException.java
// @SuppressWarnings("unused")
// public class CommandException extends Exception {
//   private static final long serialVersionUID = 1L;
// 
//   public CommandException() {
//     super();
//   }
// 
//   public CommandException(String message, Throwable cause,
//       boolean enableSuppression, boolean writableStackTrace) {
//     super(message, cause, enableSuppression, writableStackTrace);
//   }
// 
//   public CommandException(String message, Throwable cause) {
//     super(message, cause);
//   }
// 
//   public CommandException(String message) {
//     super(message);
//   }
// 
//   public CommandException(Throwable cause) {
//     super(cause);
//   }
// }
// 
// Path: core/src/main/java/me/gregorias/dfuntest/Environment.java
// public interface Environment {
// 
//   /**
//    * Copies specified file or directory to destination directory on environment.
//    *
//    * @param srcPath Local source path.
//    * @param destRelPath Relative destination directory on this environment.
//    */
//   void copyFilesFromLocalDisk(Path srcPath, String destRelPath) throws IOException;
// 
//   /**
//    * Copies specified file or directory to destination directory on local host.
//    *
//    * @param srcRelPath Relative source path on this environment.
//    * @param destPath Local destination directory.
//    */
//   void copyFilesToLocalDisk(String srcRelPath, Path destPath) throws IOException;
// 
//   /**
//    * @return Hostname of this environment. May be IP address, WWW address etc.
//    */
//   String getHostname();
// 
//   /**
//    * @return Numeric identifier of this environment
//    */
//   int getId();
// 
//   /**
//    * @return Human readable name for this environment
//    */
//   String getName();
// 
//   /**
//    * Gets saved property.
//    *
//    * @param key property's key
//    * @return value corresponding to given key
//    * @throws NoSuchElementException thrown when given key is not present
//    */
//   Object getProperty(String key) throws NoSuchElementException;
// 
//   /**
//    * Synchronously runs arbitrary command on this environment's OS.
//    *
//    * @param command Command to run
//    * @return Finished process.
//    */
//   RemoteProcess runCommand(List<String> command) throws InterruptedException, IOException;
// 
//   RemoteProcess runCommandAsynchronously(List<String> command) throws IOException;
// 
//   /**
//    * Removes specified file. If path targets a directory its content is removed as well.
//    *
//    * @param relPath Relative path to file on this environment.
//    */
//   void removeFile(String relPath) throws InterruptedException, IOException;
// 
//   /**
//    * Set arbitrary property of this environment.
//    *
//    * @param key property's key
//    * @param value property's value
//    */
//   void setProperty(String key, Object value);
// }
// 
// Path: core/src/main/java/me/gregorias/dfuntest/RemoteProcess.java
// @SuppressWarnings("unused")
// public interface RemoteProcess {
//   /**
//    * Forcibly terminates the process.
//    */
//   void destroy() throws IOException;
// 
//   InputStream getErrorStream();
// 
//   InputStream getInputStream();
// 
//   OutputStream getOutputStream();
// 
//   /**
//    * Blocks till process finishes and returns its exit code.
//    *
//    * @return exit value of process
//    */
//   int waitFor() throws InterruptedException, IOException;
// }


import me.gregorias.dfuntest.App;
import me.gregorias.dfuntest.CommandException;
import me.gregorias.dfuntest.Environment;
import me.gregorias.dfuntest.RemoteProcess;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.EOFException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
import java.util.List;
  public ExampleApp(int id, String name, Environment environment) {
    super(id, name);
    mEnvironment = environment;
    int localPort = (Integer) mEnvironment.getProperty(LOCAL_PORT_ENV_FIELD);
    mThisAppSocketAddress = new InetSocketAddress(mEnvironment.getHostname(), localPort);
    mServerHostname = (String) mEnvironment.getProperty(SERVER_HOSTNAME_ENV_FIELD);
    mServerPort = (Integer) mEnvironment.getProperty(SERVER_PORT_ENV_FIELD);
  }

  @Override
  public Environment getEnvironment() {
    return mEnvironment;
  }

  public List<Integer> getPingedIDs() throws IOException {
    ByteBuffer getIDMessage = ByteBuffer.allocateDirect(1);
    getIDMessage.put(PingApplication.GET_ID_TYPE);
    getIDMessage.flip();
    return sendMessageAndGetResponse(getIDMessage);
  }

  public void ping(int id) throws IOException {
    ByteBuffer pingMessage = ByteBuffer.allocateDirect(PingApplication.PING_MESSAGE_LENGTH);
    pingMessage.put(PingApplication.PING_TYPE);
    pingMessage.putInt(id);
    pingMessage.flip();
    sendMessageAndGetResponse(pingMessage);
  }

  @Override
// Path: core/src/main/java/me/gregorias/dfuntest/App.java
// public abstract class App<EnvironmentT extends Environment> {
//   private final int mId;
//   private final String mName;
// 
//   public App(int id, String name) {
//     mId = id;
//     mName = name;
//   }
// 
//   /**
//    * @return Underlying environment
//    */
//   public abstract EnvironmentT getEnvironment();
// 
//   /**
//    * @return Number identifying this application.
//    */
//   public int getId() {
//     return mId;
//   }
// 
//   /**
//    * @return Human readable name of this application.
//    */
//   public String getName() {
//     return mName;
//   }
// 
//   /**
//    * Starts the application and allows it to run in background.
//    */
//   @SuppressWarnings("unused")
//   public abstract void startUp() throws CommandException, IOException;
// 
//   /**
//    * Shuts down started application and deallocates all resources associated
//    * with running application.
//    */
//   @SuppressWarnings("unused")
//   public abstract void shutDown() throws IOException, InterruptedException;
// }
// 
// Path: core/src/main/java/me/gregorias/dfuntest/CommandException.java
// @SuppressWarnings("unused")
// public class CommandException extends Exception {
//   private static final long serialVersionUID = 1L;
// 
//   public CommandException() {
//     super();
//   }
// 
//   public CommandException(String message, Throwable cause,
//       boolean enableSuppression, boolean writableStackTrace) {
//     super(message, cause, enableSuppression, writableStackTrace);
//   }
// 
//   public CommandException(String message, Throwable cause) {
//     super(message, cause);
//   }
// 
//   public CommandException(String message) {
//     super(message);
//   }
// 
//   public CommandException(Throwable cause) {
//     super(cause);
//   }
// }
// 
// Path: core/src/main/java/me/gregorias/dfuntest/Environment.java
// public interface Environment {
// 
//   /**
//    * Copies specified file or directory to destination directory on environment.
//    *
//    * @param srcPath Local source path.
//    * @param destRelPath Relative destination directory on this environment.
//    */
//   void copyFilesFromLocalDisk(Path srcPath, String destRelPath) throws IOException;
// 
//   /**
//    * Copies specified file or directory to destination directory on local host.
//    *
//    * @param srcRelPath Relative source path on this environment.
//    * @param destPath Local destination directory.
//    */
//   void copyFilesToLocalDisk(String srcRelPath, Path destPath) throws IOException;
// 
//   /**
//    * @return Hostname of this environment. May be IP address, WWW address etc.
//    */
//   String getHostname();
// 
//   /**
//    * @return Numeric identifier of this environment
//    */
//   int getId();
// 
//   /**
//    * @return Human readable name for this environment
//    */
//   String getName();
// 
//   /**
//    * Gets saved property.
//    *
//    * @param key property's key
//    * @return value corresponding to given key
//    * @throws NoSuchElementException thrown when given key is not present
//    */
//   Object getProperty(String key) throws NoSuchElementException;
// 
//   /**
//    * Synchronously runs arbitrary command on this environment's OS.
//    *
//    * @param command Command to run
//    * @return Finished process.
//    */
//   RemoteProcess runCommand(List<String> command) throws InterruptedException, IOException;
// 
//   RemoteProcess runCommandAsynchronously(List<String> command) throws IOException;
// 
//   /**
//    * Removes specified file. If path targets a directory its content is removed as well.
//    *
//    * @param relPath Relative path to file on this environment.
//    */
//   void removeFile(String relPath) throws InterruptedException, IOException;
// 
//   /**
//    * Set arbitrary property of this environment.
//    *
//    * @param key property's key
//    * @param value property's value
//    */
//   void setProperty(String key, Object value);
// }
// 
// Path: core/src/main/java/me/gregorias/dfuntest/RemoteProcess.java
// @SuppressWarnings("unused")
// public interface RemoteProcess {
//   /**
//    * Forcibly terminates the process.
//    */
//   void destroy() throws IOException;
// 
//   InputStream getErrorStream();
// 
//   InputStream getInputStream();
// 
//   OutputStream getOutputStream();
// 
//   /**
//    * Blocks till process finishes and returns its exit code.
//    *
//    * @return exit value of process
//    */
//   int waitFor() throws InterruptedException, IOException;
// }


// Path: example/src/main/java/me/gregorias/dfuntest/example/ExampleApp.java
import me.gregorias.dfuntest.App;
import me.gregorias.dfuntest.CommandException;
import me.gregorias.dfuntest.Environment;
import me.gregorias.dfuntest.RemoteProcess;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.EOFException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
import java.util.List;

  public ExampleApp(int id, String name, Environment environment) {
    super(id, name);
    mEnvironment = environment;
    int localPort = (Integer) mEnvironment.getProperty(LOCAL_PORT_ENV_FIELD);
    mThisAppSocketAddress = new InetSocketAddress(mEnvironment.getHostname(), localPort);
    mServerHostname = (String) mEnvironment.getProperty(SERVER_HOSTNAME_ENV_FIELD);
    mServerPort = (Integer) mEnvironment.getProperty(SERVER_PORT_ENV_FIELD);
  }

  @Override
  public Environment getEnvironment() {
    return mEnvironment;
  }

  public List<Integer> getPingedIDs() throws IOException {
    ByteBuffer getIDMessage = ByteBuffer.allocateDirect(1);
    getIDMessage.put(PingApplication.GET_ID_TYPE);
    getIDMessage.flip();
    return sendMessageAndGetResponse(getIDMessage);
  }

  public void ping(int id) throws IOException {
    ByteBuffer pingMessage = ByteBuffer.allocateDirect(PingApplication.PING_MESSAGE_LENGTH);
    pingMessage.put(PingApplication.PING_TYPE);
    pingMessage.putInt(id);
    pingMessage.flip();
    sendMessageAndGetResponse(pingMessage);
  }

  @Override
  public synchronized void startUp() throws CommandException, IOException {