package dslab.protocol; import dslab.authentication.AuthenticationException; import dslab.mailbox.MessageNotFoundException; import dslab.protocol.dmtp.InvalidMessageException; import dslab.protocol.dmtp.NoOpenMessageException; import dslab.rmi.serialize.IllegalCommandException; import dslab.rmi.serialize.MissingArgumentException; import dslab.rmi.serialize.ParseException; import dslab.routing.AddressResolutionException; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; /** * The business code equivalent to a DMTP/DMAP response of kind "error ...". * The code is meant to be encoded into the protocol response. It should identify the message * so it can be deserialized at the client and must not contain any spaces */ public abstract class ProtocolException extends Exception { private static final Logger LOG = Logger.getLogger(ProtocolException.class.getSimpleName()); private static final Map> code_exception; static { code_exception = new ConcurrentHashMap<>(); List.of( AuthenticationException.class, InvalidMessageException.class, NoOpenMessageException.class, IllegalCommandException.class, MissingArgumentException.class, ParseException.class, MessageNotFoundException.class ).forEach(ProtocolException::registerStringRepresentationForException); registerStringRepresentationForException("unknown", AddressResolutionException.class); } /** * When exceptions occur during communication via DMTP or DMAP, the exception is encoded as a string * in the format "error " * For example, the client might try to edit a message before sending "begin", which causes a * NoOpenMessageException to occur. The server answers * "error NoOpenMessageException begin message first". *

* We can't always use the exceptions class name as its string representation because * in some cases the DMTP spec requires responses different from the exception name, i.e. * "error unknown Invalid Credentials" for an AddressResolutionException. */ protected static void registerStringRepresentationForException(String stringRepresentation, Class exceptionClass) { code_exception.put(stringRepresentation, exceptionClass); } protected static void registerStringRepresentationForException(Class exceptionClass) { registerStringRepresentationForException(exceptionClass.getSimpleName().toLowerCase(), exceptionClass); } private String message = ""; public ProtocolException() {super();} public ProtocolException(String message) { this.message = message; } @Override public String getMessage() { return message; } public String getCode() { return this.getClass().getSimpleName().toLowerCase(); } public ProtocolException(String message, Exception cause) {super(message, cause);} public static ProtocolException from(String code, String message) { try { Class exceptionClass = code_exception .entrySet() .stream() .filter(entry -> entry.getKey().equalsIgnoreCase(code)) .findFirst() .orElseThrow(() -> new IllegalArgumentException( "Didn't find a class for code '" + code + "'. " + "Did you register the exception?")) .getValue(); ProtocolException protocolException; try { //if a no-arg constructor exists we use that one. protocolException = (ProtocolException) exceptionClass.getConstructor().newInstance(); return protocolException; } catch (NoSuchMethodException ignore){ LOG.finer("No no-arg constructor found"); } //else try the constructor with one message parameter protocolException = (ProtocolException) exceptionClass.getConstructor(String.class).newInstance(message); return protocolException; } catch (Exception e) { throw new RuntimeException(e); } } protected void setMessage(String message) { this.message = message; } }