Implement skeleton for MailboxServer

This commit is contained in:
Tobias Eidelpes 2020-11-11 18:09:05 +01:00
parent 29ce5d7875
commit 2b0b33baea
6 changed files with 270 additions and 4 deletions

View File

@ -2,6 +2,8 @@ package dslab;
import dslab.exception.MalformedInputException; import dslab.exception.MalformedInputException;
import java.util.Objects;
public class Email { public class Email {
private String username; private String username;
private String domain; private String domain;
@ -43,4 +45,18 @@ public class Email {
public String toString() { public String toString() {
return username + '@' + domain; return username + '@' + domain;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Email email = (Email) o;
return Objects.equals(getUsername(), email.getUsername()) &&
Objects.equals(getDomain(), email.getDomain());
}
@Override
public int hashCode() {
return Objects.hash(getUsername(), getDomain());
}
} }

View File

@ -0,0 +1,4 @@
package dslab.mailbox;
public class DMAPConnection {
}

View File

@ -0,0 +1,12 @@
package dslab.mailbox;
import dslab.Message;
import dslab.transfer.ClientConnection;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.logging.Logger;
public class DMAPListener {
}

View File

@ -0,0 +1,142 @@
package dslab.mailbox;
import dslab.Email;
import dslab.Message;
import dslab.exception.MalformedInputException;
import dslab.exception.UnknownRecipientException;
import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
public class DMTPConnection implements Runnable {
Logger logger = Logger.getLogger(DMTPConnection.class.getName());
private final Socket socket;
private PrintWriter out;
private BufferedReader in;
private Message msg = new Message();
private final ConcurrentHashMap<Email, LinkedList<Message>> messageStorage;
public DMTPConnection(Socket connection, ConcurrentHashMap<Email, LinkedList<Message>> messageStorage) {
this.socket = connection;
this.messageStorage = messageStorage;
}
@Override
public void run() {
logger.finer("Preparing for DMTP communication in " + this.toString());
try {
this.out = new PrintWriter(socket.getOutputStream(), true);
this.in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out.println("ok DMTP");
String userInput;
try {
if (!("begin".equals(in.readLine()))) {
out.println("error protocol error");
shutdown();
}
} catch (SocketException e) {
// Thrown if socket has already been closed by shutdown() method
logger.finer("Received interrupt. Exiting " + this.toString());
shutdown();
}
out.println("ok");
while (!Thread.currentThread().isInterrupted() && (userInput = in.readLine()) != null) {
if ("quit".equals(userInput)) {
out.println("ok bye");
shutdown();
} else if ("send".equals(userInput)) {
try {
storeMessage();
out.println("ok");
} catch (UnknownRecipientException e) {
out.println(e.getMessage());
shutdown();
}
} else if ("to".equals(userInput.split("\\s+")[0])) {
msg.setTo(new ArrayList<>());
String[] emailAddresses = userInput.split("\\s+")[1].split(",");
int count = 0;
try {
for (String emailAddress : emailAddresses) {
msg.addTo(new Email(emailAddress));
count++;
}
out.println("ok " + count);
} catch (MalformedInputException mie) {
out.println(mie.getMessage());
}
} else if ("from".equals(userInput.split("\\s+")[0])) {
try {
Email from = new Email(userInput.split("\\s+")[1]);
this.msg.setFrom(from);
out.println("ok");
} catch (MalformedInputException mie) {
out.println(mie.getMessage());
}
} else if ("subject".equals(userInput.split("\\s+")[0])) {
String subject = "";
if (userInput.split("\\s+").length > 1)
subject = userInput.split("\\s+", 2)[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+", 2)[1];
logger.info("Setting data to: " + data);
msg.setData(data);
out.println("ok");
} else {
out.println("error protocol error");
shutdown();
}
}
} catch (InterruptedIOException ioe) {
logger.info("Received interrupt from parent. Shutting down...");
shutdown();
} catch (IOException e) {
logger.severe("Failed to get IO-Stream");
e.printStackTrace();
shutdown();
}
}
private synchronized void storeMessage() throws UnknownRecipientException {
String errorUnknownRecipient = "";
for (Email recipient : this.msg.getTo()) {
if (this.messageStorage.containsKey(recipient)) {
this.messageStorage.get(recipient).add(this.msg);
} else {
if (errorUnknownRecipient.isBlank())
errorUnknownRecipient = "error unknown recipient " + recipient.getUsername();
}
}
if (!errorUnknownRecipient.isBlank())
throw new UnknownRecipientException(errorUnknownRecipient);
this.msg = new Message();
}
public void shutdown() {
logger.info("Shutting down client connection " + this.toString());
try {
if (socket != null)
socket.close();
in.close();
out.close();
} catch (IOException e) {
logger.severe("Error closing socket and/or IO-streams");
e.printStackTrace();
}
Thread.currentThread().interrupt();
}
}

View File

@ -0,0 +1,71 @@
package dslab.mailbox;
import dslab.Email;
import dslab.Message;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
public class DMTPListener extends Thread {
private final ServerSocket serverSocket;
private final Logger logger = Logger.getLogger(DMTPListener.class.getName());
private final ArrayList<DMTPConnection> clients = new ArrayList<>();
private final ExecutorService executorService = Executors.newCachedThreadPool();
private final ConcurrentHashMap<Email, LinkedList<Message>> storage;
public DMTPListener(ServerSocket serverSocket, ConcurrentHashMap<Email, LinkedList<Message>> storage) {
this.serverSocket = serverSocket;
this.storage = storage;
}
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
logger.finer("Waiting for request on serverSocket " + serverSocket.toString());
try {
Socket s = serverSocket.accept();
logger.fine("Processing incoming socket " + s.toString());
DMTPConnection dmtpConnection = new DMTPConnection(s, storage);
clients.add(dmtpConnection);
executorService.submit(dmtpConnection);
} catch (InterruptedIOException | SocketException e) {
logger.finer("Received interrupt. Exiting " + this.toString());
this.shutdown();
} catch (IOException e) {
logger.severe("Error starting serverSocket " + serverSocket.toString());
e.printStackTrace();
this.shutdown();
}
}
}
public void shutdown() {
logger.finer("Shutting down DMTPHandler " + this.toString());
for (DMTPConnection client : clients) {
if (client != null)
client.shutdown();
}
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();
}
this.interrupt();
}
}

View File

@ -4,21 +4,28 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.util.LinkedList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger; import java.util.logging.Logger;
import at.ac.tuwien.dsg.orvell.Shell; import at.ac.tuwien.dsg.orvell.Shell;
import at.ac.tuwien.dsg.orvell.StopShellException; import at.ac.tuwien.dsg.orvell.StopShellException;
import at.ac.tuwien.dsg.orvell.annotation.Command; import at.ac.tuwien.dsg.orvell.annotation.Command;
import dslab.ComponentFactory; import dslab.ComponentFactory;
import dslab.Email;
import dslab.Message;
import dslab.util.Config; import dslab.util.Config;
public class MailboxServer implements IMailboxServer, Runnable { public class MailboxServer implements IMailboxServer, Runnable {
private static final Logger logger = Logger.getLogger(MailboxServer.class.getName()); private static final Logger logger = Logger.getLogger(MailboxServer.class.getName());
private final String domain;
private ServerSocket dmtpServerSocket; private ServerSocket dmtpServerSocket;
private ServerSocket dmapServerSocket; private ServerSocket dmapServerSocket;
private final Shell shell; private final Shell shell;
private final Integer dmtpServerPort; private final Integer dmtpServerPort;
private final Integer dmapServerPort; private final Integer dmapServerPort;
private final ConcurrentHashMap<Email, LinkedList<Message>> messageStorage = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, String> userStorage = new ConcurrentHashMap<>();
/** /**
* Creates a new server instance. * Creates a new server instance.
@ -29,7 +36,19 @@ public class MailboxServer implements IMailboxServer, Runnable {
* @param out the output stream to write console output to * @param out the output stream to write console output to
*/ */
public MailboxServer(String componentId, Config config, InputStream in, PrintStream out) { public MailboxServer(String componentId, Config config, InputStream in, PrintStream out) {
// TODO initialize email and user storage (concurrent hashmap?) this.domain = config.getString("domain");
Config userConfig = new Config(config.getString("users.config"));
// Load users from config into userStorage for authentication
for (String key : userConfig.listKeys()) {
userStorage.put(key, userConfig.getString(key));
}
// Load Email Addresses into messageStorage
for (String key : userStorage.keySet()) {
Email current = new Email(key, domain);
messageStorage.put(current, new LinkedList<>());
}
this.shell = new Shell(in, out); this.shell = new Shell(in, out);
this.shell.register(this); this.shell.register(this);
this.shell.setPrompt("Mailboxserver> "); this.shell.setPrompt("Mailboxserver> ");
@ -39,7 +58,7 @@ public class MailboxServer implements IMailboxServer, Runnable {
@Override @Override
public void run() { public void run() {
logger.info("Creating DMTP serverSocket for TransferServer + " + this.toString()); logger.info("Creating DMTP and DMAP serverSockets for MailboxServer + " + this.toString());
try { try {
this.dmtpServerSocket = new ServerSocket(dmtpServerPort); this.dmtpServerSocket = new ServerSocket(dmtpServerPort);
this.dmapServerSocket = new ServerSocket(dmapServerPort); this.dmapServerSocket = new ServerSocket(dmapServerPort);
@ -48,14 +67,16 @@ public class MailboxServer implements IMailboxServer, Runnable {
e.printStackTrace(); e.printStackTrace();
shutdown(); shutdown();
} }
// TODO spawn listener for transfer servers // TODO spawn listener for transfer servers (DMTPListener)
// TODO spawn listener for user clients // TODO spawn listener for user clients (DMAPListener)
this.shell.run(); this.shell.run();
} }
@Command @Command
@Override @Override
public void shutdown() { public void shutdown() {
// TODO shutdown DMTPListener
// TODO shutdown DMAPListener
try { try {
if (dmtpServerSocket != null) if (dmtpServerSocket != null)
dmtpServerSocket.close(); dmtpServerSocket.close();