From 56158f9a697c9d41298839c86d133537c6b81e1f Mon Sep 17 00:00:00 2001 From: Tobias Eidelpes Date: Sat, 17 Oct 2020 18:04:18 +0200 Subject: [PATCH] Implement TransferServer DMTP --- src/main/java/dslab/Email.java | 46 ++++++++ src/main/java/dslab/Message.java | 65 +++++++++++ .../exception/MalformedInputException.java | 7 ++ .../exception/MissingInputException.java | 7 ++ .../exception/UnknownRecipientException.java | 7 ++ .../java/dslab/transfer/ClientConnection.java | 105 ++++++++++++++++++ .../java/dslab/transfer/TransferServer.java | 65 ++++++++++- 7 files changed, 299 insertions(+), 3 deletions(-) create mode 100644 src/main/java/dslab/Email.java create mode 100644 src/main/java/dslab/Message.java create mode 100644 src/main/java/dslab/exception/MalformedInputException.java create mode 100644 src/main/java/dslab/exception/MissingInputException.java create mode 100644 src/main/java/dslab/exception/UnknownRecipientException.java create mode 100644 src/main/java/dslab/transfer/ClientConnection.java diff --git a/src/main/java/dslab/Email.java b/src/main/java/dslab/Email.java new file mode 100644 index 0000000..a255687 --- /dev/null +++ b/src/main/java/dslab/Email.java @@ -0,0 +1,46 @@ +package dslab; + +import dslab.exception.MalformedInputException; + +public class Email { + private String username; + private String domain; + + public Email(String email) throws MalformedInputException { + if (email.split("@").length != 2) + throw new MalformedInputException("error email addresses must be of the form user@domain: " + email); + if (email.split("@")[0].isBlank()) + throw new MalformedInputException("error email addresses must be of the form user@domain" + email); + if (email.split("@")[1].isBlank()) + throw new MalformedInputException("error email addresses must be of the form user@domain" + email); + + this.username = email.split("@")[0]; + this.domain = email.split("@")[1]; + } + + public Email(String username, String domain) { + this.username = username; + this.domain = domain; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + @Override + public String toString() { + return username + '@' + domain; + } +} diff --git a/src/main/java/dslab/Message.java b/src/main/java/dslab/Message.java new file mode 100644 index 0000000..99cb40c --- /dev/null +++ b/src/main/java/dslab/Message.java @@ -0,0 +1,65 @@ +package dslab; + +import dslab.exception.MissingInputException; + +import java.util.ArrayList; + +public class Message { + private ArrayList to; + private Email from; + private String subject; + private String data; + + public Message() { + } + + public Message(ArrayList to, Email from, String subject, String data) { + this.to = to; + this.from = from; + this.subject = subject; + this.data = data; + } + + public void allFieldsSet() throws MissingInputException { + if (this.subject == null) this.subject = ""; + if (this.data == null) this.data = ""; + if (this.to.isEmpty()) throw new MissingInputException("error no receiver"); + if (this.from == null) throw new MissingInputException("error no sender"); + } + + public void addTo(Email email) { + to.add(email); + } + + public ArrayList getTo() { + return to; + } + + public void setTo(ArrayList to) { + this.to = to; + } + + public Email getFrom() { + return from; + } + + public void setFrom(Email from) { + this.from = from; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } +} diff --git a/src/main/java/dslab/exception/MalformedInputException.java b/src/main/java/dslab/exception/MalformedInputException.java new file mode 100644 index 0000000..aef62d9 --- /dev/null +++ b/src/main/java/dslab/exception/MalformedInputException.java @@ -0,0 +1,7 @@ +package dslab.exception; + +public class MalformedInputException extends Exception { + public MalformedInputException(String errorMessage) { + super(errorMessage); + } +} diff --git a/src/main/java/dslab/exception/MissingInputException.java b/src/main/java/dslab/exception/MissingInputException.java new file mode 100644 index 0000000..a10a133 --- /dev/null +++ b/src/main/java/dslab/exception/MissingInputException.java @@ -0,0 +1,7 @@ +package dslab.exception; + +public class MissingInputException extends Exception { + public MissingInputException(String errorMessage) { + super(errorMessage); + } +} diff --git a/src/main/java/dslab/exception/UnknownRecipientException.java b/src/main/java/dslab/exception/UnknownRecipientException.java new file mode 100644 index 0000000..f101d67 --- /dev/null +++ b/src/main/java/dslab/exception/UnknownRecipientException.java @@ -0,0 +1,7 @@ +package dslab.exception; + +public class UnknownRecipientException extends Exception { + public UnknownRecipientException(String errorMessage) { + super(errorMessage); + } +} diff --git a/src/main/java/dslab/transfer/ClientConnection.java b/src/main/java/dslab/transfer/ClientConnection.java new file mode 100644 index 0000000..746bee2 --- /dev/null +++ b/src/main/java/dslab/transfer/ClientConnection.java @@ -0,0 +1,105 @@ +package dslab.transfer; + +import dslab.Email; +import dslab.Message; +import dslab.exception.MalformedInputException; +import dslab.exception.MissingInputException; +import dslab.exception.UnknownRecipientException; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.Socket; +import java.util.logging.Logger; + +public class ClientConnection implements Runnable { + Logger logger = Logger.getLogger(ClientConnection.class.getName()); + private Socket socket; + + private Message msg; + + public ClientConnection(Socket connection) { + this.socket = connection; + } + + @Override + public void run() { + try { + PrintWriter out = new PrintWriter(socket.getOutputStream(), true); + BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + out.println("ok DMTP"); + + String userInput; + + if (!("begin".equals(in.readLine()))) { + out.println("error protocol error"); + shutdown(); + } + + while ((userInput = in.readLine()) != null) { + if ("quit".equals(userInput)) { + out.println("ok bye"); + shutdown(); + } else if ("send".equals(userInput)) { + try { + sendMessage(); + out.println("ok"); + } catch (MissingInputException e) { + out.println(e.getMessage()); + } + } else if ("to".equals(userInput.split("\\s+")[0])) { + String[] emailAddresses = userInput.split("\\s+")[1].split(","); + int count = 0; + for (String emailAddress : emailAddresses) { + try { + msg.addTo(new Email(emailAddress)); + count++; + } catch (MalformedInputException mie) { + out.println(mie.getMessage()); + } + } + out.println("ok " + count); + } else if ("from".equals(userInput.split("\\s+")[0])) { + try { + msg.setFrom(new Email(userInput.split("\\s+")[1])); + } catch (MalformedInputException mie) { + out.println(mie.getMessage()); + } + out.println("ok"); + } else if ("subject".equals(userInput.split("\\s+")[0])) { + String subject = userInput.split("\\s+", 1)[1]; + logger.info("Setting subject to: " + subject); + msg.setSubject(subject); + out.println("ok"); + } else if ("data".equals(userInput.split("\\s+")[0])) { + String data = userInput.split("\\s+", 1)[1]; + logger.info("Setting data to: " + data); + msg.setData(data); + out.println("ok"); + } else { + out.println("error protocol error"); + shutdown(); + } + } + } catch (IOException e) { + logger.severe("Failed to get IO-Stream"); + e.printStackTrace(); + shutdown(); + } + } + + public void shutdown() { + try { + socket.close(); + } catch (IOException e) { + logger.severe("Error closing socket " + socket.toString()); + e.printStackTrace(); + } + } + + public void sendMessage() throws MissingInputException { + msg.allFieldsSet(); + // TODO send message to mailbox servers or submit to asynchronous queue + } +} diff --git a/src/main/java/dslab/transfer/TransferServer.java b/src/main/java/dslab/transfer/TransferServer.java index e4e9c89..daf5613 100644 --- a/src/main/java/dslab/transfer/TransferServer.java +++ b/src/main/java/dslab/transfer/TransferServer.java @@ -1,12 +1,29 @@ package dslab.transfer; +import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; +import at.ac.tuwien.dsg.orvell.Shell; import dslab.ComponentFactory; import dslab.util.Config; public class TransferServer implements ITransferServer, Runnable { + Logger logger = Logger.getLogger(TransferServer.class.getName()); + + private ServerSocket serverSocket; + private Integer serverPort; + + private ExecutorService executorService = Executors.newFixedThreadPool(20); + + private ArrayList mailboxServers; /** * Creates a new server instance. @@ -17,17 +34,59 @@ public class TransferServer implements ITransferServer, Runnable { * @param out the output stream to write console output to */ public TransferServer(String componentId, Config config, InputStream in, PrintStream out) { - // TODO + Config earthPlanet = new Config("mailbox-earth-planet"); + Config univerZe = new Config("mailbox-univer-ze"); + mailboxServers.add(earthPlanet); + mailboxServers.add(univerZe); + Shell shell = new Shell().register("quit", (input, context) -> shutdown()); + this.serverPort = config.getInt("tcp.port"); + + shell.run(); } @Override public void run() { - // TODO + try { + logger.info("Creating serverSocket for " + this.toString()); + serverSocket = new ServerSocket(serverPort); + + // TODO stop server on quit command over shell + while (true) { + logger.info("Waiting for request on serverSocket " + serverSocket.toString()); + try { + Socket s = serverSocket.accept(); + logger.info("Processing incoming socket " + s.toString()); + executorService.submit(new ClientConnection(s)); + } catch (IOException ioe) { + logger.severe("Error starting serverSocket " + serverSocket.toString()); + ioe.printStackTrace(); + } + } + } catch (IOException e) { + logger.severe("Error creating serverSocket " + serverSocket.toString()); + e.printStackTrace(); + } } @Override public void shutdown() { - // TODO + try { + serverSocket.close(); + } catch (IOException e) { + logger.severe("Error closing serverSocket " + serverSocket.toString()); + e.printStackTrace(); + } + executorService.shutdown(); + try { + if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { + executorService.shutdownNow(); + if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) + System.err.println("Pool did not terminate"); + } + } catch (InterruptedException ie) { + executorService.shutdownNow(); + Thread.currentThread().interrupt(); + } } public static void main(String[] args) throws Exception {