summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-06-20 18:47:45 +0300
committerPaul Buetow <paul@buetow.org>2025-06-20 18:47:45 +0300
commit70fc0505b223f7bf17d3671d0532773359cf7858 (patch)
tree1c244371e6a4e89e7694d5691db6a14b0ba2da06
parentf6d2a6bbbc37c552accf91a13ccd6ea45ecf8e73 (diff)
Implement proper exception hierarchy and consistent error handling
- Create base exception classes: - VSSimulatorException: Base checked exception for all simulator errors - VSSimulatorRuntimeException: Base unchecked exception for unrecoverable errors - Refactor existing exceptions to extend from base class: - VSEventNotCopyableException: Now includes better error messages - VSParseIntegerVectorException: Added constructors with cause support - VSNegativeNumberException: Added field name and value parameters - Add new specific exception types: - VSConfigurationException: For configuration errors - VSSerializationException: For save/load operations - VSProcessException: For process-related errors - VSProtocolException: For protocol execution errors - Create VSErrorHandler utility class: - Centralized error logging and user notification - Consistent error handling patterns - Helper methods for warning and error dialogs - Update exception handling in code: - VSClassLoader: Better error messages and specific exception handling - Timestamp events: Catch RuntimeException for custom actions - All 132 unit tests pass 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
-rw-r--r--src/main/java/events/implementations/VSLamportTimestampEvent.java4
-rw-r--r--src/main/java/events/implementations/VSVectorTimestampEvent.java4
-rw-r--r--src/main/java/exceptions/VSConfigurationException.java31
-rw-r--r--src/main/java/exceptions/VSErrorHandler.java148
-rw-r--r--src/main/java/exceptions/VSEventNotCopyableException.java15
-rw-r--r--src/main/java/exceptions/VSNegativeNumberException.java32
-rw-r--r--src/main/java/exceptions/VSParseIntegerVectorException.java33
-rw-r--r--src/main/java/exceptions/VSProcessException.java56
-rw-r--r--src/main/java/exceptions/VSProtocolException.java56
-rw-r--r--src/main/java/exceptions/VSSerializationException.java42
-rw-r--r--src/main/java/exceptions/VSSimulatorException.java47
-rw-r--r--src/main/java/exceptions/VSSimulatorRuntimeException.java47
-rw-r--r--src/main/java/utils/VSClassLoader.java22
13 files changed, 518 insertions, 19 deletions
diff --git a/src/main/java/events/implementations/VSLamportTimestampEvent.java b/src/main/java/events/implementations/VSLamportTimestampEvent.java
index 272ea06..28da7dc 100644
--- a/src/main/java/events/implementations/VSLamportTimestampEvent.java
+++ b/src/main/java/events/implementations/VSLamportTimestampEvent.java
@@ -73,8 +73,10 @@ public class VSLamportTimestampEvent extends VSTimestampTriggeredEvent {
if (customAction != null) {
try {
customAction.run();
- } catch (Exception e) {
+ } catch (RuntimeException e) {
+ // Log the error but don't let it stop the event processing
internalProcess.log("Error executing custom action: " + e.getMessage());
+ exceptions.VSErrorHandler.warning("Lamport timestamp event custom action failed", e);
}
}
diff --git a/src/main/java/events/implementations/VSVectorTimestampEvent.java b/src/main/java/events/implementations/VSVectorTimestampEvent.java
index 4d3b327..f54b3f7 100644
--- a/src/main/java/events/implementations/VSVectorTimestampEvent.java
+++ b/src/main/java/events/implementations/VSVectorTimestampEvent.java
@@ -74,8 +74,10 @@ public class VSVectorTimestampEvent extends VSTimestampTriggeredEvent {
if (customAction != null) {
try {
customAction.run();
- } catch (Exception e) {
+ } catch (RuntimeException e) {
+ // Log the error but don't let it stop the event processing
internalProcess.log("Error executing custom action: " + e.getMessage());
+ exceptions.VSErrorHandler.warning("Vector timestamp event custom action failed", e);
}
}
diff --git a/src/main/java/exceptions/VSConfigurationException.java b/src/main/java/exceptions/VSConfigurationException.java
new file mode 100644
index 0000000..d37eeeb
--- /dev/null
+++ b/src/main/java/exceptions/VSConfigurationException.java
@@ -0,0 +1,31 @@
+package exceptions;
+
+/**
+ * Exception thrown when there is a configuration error in the simulator.
+ * This includes invalid preferences, missing required settings, or conflicting configurations.
+ *
+ * @author Paul C. Buetow
+ */
+public class VSConfigurationException extends VSSimulatorException {
+ /** The serial version uid */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs a new configuration exception with the specified detail message.
+ *
+ * @param message the detail message
+ */
+ public VSConfigurationException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new configuration exception with the specified detail message and cause.
+ *
+ * @param message the detail message
+ * @param cause the cause
+ */
+ public VSConfigurationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/exceptions/VSErrorHandler.java b/src/main/java/exceptions/VSErrorHandler.java
new file mode 100644
index 0000000..59085f0
--- /dev/null
+++ b/src/main/java/exceptions/VSErrorHandler.java
@@ -0,0 +1,148 @@
+package exceptions;
+
+import javax.swing.JOptionPane;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Centralized error handling utility for the DS-Sim application.
+ * Provides consistent error logging and user notification.
+ *
+ * @author Paul C. Buetow
+ */
+public final class VSErrorHandler {
+
+ private static final Logger LOGGER = Logger.getLogger(VSErrorHandler.class.getName());
+
+ /** Private constructor to prevent instantiation */
+ private VSErrorHandler() {}
+
+ /**
+ * Handle an exception with logging and optional user notification.
+ *
+ * @param exception the exception to handle
+ * @param showDialog whether to show a dialog to the user
+ */
+ public static void handle(Exception exception, boolean showDialog) {
+ // Log the exception
+ LOGGER.log(Level.SEVERE, exception.getMessage(), exception);
+
+ // Show dialog if requested
+ if (showDialog) {
+ showErrorDialog(exception);
+ }
+ }
+
+ /**
+ * Handle an exception with logging only (no user notification).
+ *
+ * @param exception the exception to handle
+ */
+ public static void handle(Exception exception) {
+ handle(exception, false);
+ }
+
+ /**
+ * Handle an exception with a custom message.
+ *
+ * @param message custom message to log and display
+ * @param exception the exception to handle
+ * @param showDialog whether to show a dialog to the user
+ */
+ public static void handle(String message, Exception exception, boolean showDialog) {
+ // Log with custom message
+ LOGGER.log(Level.SEVERE, message, exception);
+
+ // Show dialog if requested
+ if (showDialog) {
+ showErrorDialog(message, exception);
+ }
+ }
+
+ /**
+ * Log a warning without throwing an exception.
+ *
+ * @param message the warning message
+ */
+ public static void warning(String message) {
+ LOGGER.log(Level.WARNING, message);
+ }
+
+ /**
+ * Log a warning with an associated exception.
+ *
+ * @param message the warning message
+ * @param exception the associated exception
+ */
+ public static void warning(String message, Exception exception) {
+ LOGGER.log(Level.WARNING, message, exception);
+ }
+
+ /**
+ * Show an error dialog to the user.
+ *
+ * @param exception the exception to display
+ */
+ private static void showErrorDialog(Exception exception) {
+ String title = "Simulator Error";
+ String message = exception.getMessage();
+
+ if (exception instanceof VSSimulatorException) {
+ // Use more specific title for our exceptions
+ if (exception instanceof VSConfigurationException) {
+ title = "Configuration Error";
+ } else if (exception instanceof VSProcessException) {
+ title = "Process Error";
+ } else if (exception instanceof VSProtocolException) {
+ title = "Protocol Error";
+ } else if (exception instanceof VSSerializationException) {
+ title = "Save/Load Error";
+ }
+ }
+
+ if (message == null || message.isEmpty()) {
+ message = "An unexpected error occurred: " + exception.getClass().getSimpleName();
+ }
+
+ JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE);
+ }
+
+ /**
+ * Show an error dialog with a custom message.
+ *
+ * @param message the custom message
+ * @param exception the exception that occurred
+ */
+ private static void showErrorDialog(String message, Exception exception) {
+ String details = exception.getMessage();
+ if (details != null && !details.isEmpty()) {
+ message = message + "\n\nDetails: " + details;
+ }
+
+ JOptionPane.showMessageDialog(null, message, "Simulator Error", JOptionPane.ERROR_MESSAGE);
+ }
+
+ /**
+ * Convert a generic exception to a simulator exception if possible.
+ *
+ * @param e the exception to convert
+ * @param context additional context about where the error occurred
+ * @return a VSSimulatorException wrapping the original exception
+ */
+ public static VSSimulatorException wrap(Exception e, String context) {
+ if (e instanceof VSSimulatorException) {
+ return (VSSimulatorException) e;
+ }
+
+ // Wrap common exceptions with more specific types
+ if (e instanceof java.io.IOException) {
+ return new VSSerializationException(context, e);
+ } else if (e instanceof NumberFormatException) {
+ return new VSConfigurationException("Invalid number format in " + context, e);
+ } else if (e instanceof NullPointerException) {
+ return new VSSimulatorException("Null value encountered in " + context, e);
+ } else {
+ return new VSSimulatorException(context + ": " + e.getMessage(), e);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/exceptions/VSEventNotCopyableException.java b/src/main/java/exceptions/VSEventNotCopyableException.java
index f542db8..b555713 100644
--- a/src/main/java/exceptions/VSEventNotCopyableException.java
+++ b/src/main/java/exceptions/VSEventNotCopyableException.java
@@ -1,16 +1,21 @@
package exceptions;
/**
- * The Interface VSEventNotCopyableException, this exception is thrown if
- * the someone tried to copy a not copyable event!
+ * Exception thrown when attempting to copy an event that does not support copying.
+ * Events must implement VSCopyableEvent interface to be copyable.
*
* @author Paul C. Buetow
*/
-public class VSEventNotCopyableException extends Exception {
+public class VSEventNotCopyableException extends VSSimulatorException {
/** The serial version uid */
private static final long serialVersionUID = 1L;
- public VSEventNotCopyableException(String descr) {
- super(descr);
+ /**
+ * Constructs a new event not copyable exception with the specified event description.
+ *
+ * @param eventDescription description of the event that cannot be copied
+ */
+ public VSEventNotCopyableException(String eventDescription) {
+ super("Event cannot be copied: " + eventDescription);
}
}
diff --git a/src/main/java/exceptions/VSNegativeNumberException.java b/src/main/java/exceptions/VSNegativeNumberException.java
index edf2049..a2f5bf6 100644
--- a/src/main/java/exceptions/VSNegativeNumberException.java
+++ b/src/main/java/exceptions/VSNegativeNumberException.java
@@ -1,12 +1,38 @@
package exceptions;
/**
- * The Interface VSNegativeNumberException, this exception is thrown if
- * a negative number has returned if a positive number has been expected.
+ * Exception thrown when a negative number is encountered where a positive number is expected.
+ * This is typically used for validation of numeric inputs that must be non-negative.
*
* @author Paul C. Buetow
*/
-public class VSNegativeNumberException extends Exception {
+public class VSNegativeNumberException extends VSSimulatorException {
/** The serial version uid */
private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs a new negative number exception.
+ */
+ public VSNegativeNumberException() {
+ super("Negative number not allowed");
+ }
+
+ /**
+ * Constructs a new negative number exception with the specified field name.
+ *
+ * @param fieldName the name of the field that contained the negative number
+ */
+ public VSNegativeNumberException(String fieldName) {
+ super("Negative number not allowed for field: " + fieldName);
+ }
+
+ /**
+ * Constructs a new negative number exception with the specified field name and value.
+ *
+ * @param fieldName the name of the field that contained the negative number
+ * @param value the negative value that was encountered
+ */
+ public VSNegativeNumberException(String fieldName, long value) {
+ super(String.format("Negative number not allowed for field '%s': %d", fieldName, value));
+ }
}
diff --git a/src/main/java/exceptions/VSParseIntegerVectorException.java b/src/main/java/exceptions/VSParseIntegerVectorException.java
index 19c2b82..639b77e 100644
--- a/src/main/java/exceptions/VSParseIntegerVectorException.java
+++ b/src/main/java/exceptions/VSParseIntegerVectorException.java
@@ -1,13 +1,38 @@
package exceptions;
/**
- * The Interface VSParseIntegerVectorException, this exception is thrown if
- * the VSAbstractEditor is not able to parse the vector fields input of the
- * user.
+ * Exception thrown when the VSAbstractEditor cannot parse vector field input.
+ * This typically occurs when user input for vector values is malformed.
*
* @author Paul C. Buetow
*/
-public class VSParseIntegerVectorException extends Exception {
+public class VSParseIntegerVectorException extends VSSimulatorException {
/** The serial version uid */
private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs a new parse integer vector exception.
+ */
+ public VSParseIntegerVectorException() {
+ super("Failed to parse integer vector from input");
+ }
+
+ /**
+ * Constructs a new parse integer vector exception with the specified input string.
+ *
+ * @param input the input string that could not be parsed
+ */
+ public VSParseIntegerVectorException(String input) {
+ super("Failed to parse integer vector from input: " + input);
+ }
+
+ /**
+ * Constructs a new parse integer vector exception with the specified input and cause.
+ *
+ * @param input the input string that could not be parsed
+ * @param cause the underlying cause of the parse failure
+ */
+ public VSParseIntegerVectorException(String input, Throwable cause) {
+ super("Failed to parse integer vector from input: " + input, cause);
+ }
}
diff --git a/src/main/java/exceptions/VSProcessException.java b/src/main/java/exceptions/VSProcessException.java
new file mode 100644
index 0000000..3c0910b
--- /dev/null
+++ b/src/main/java/exceptions/VSProcessException.java
@@ -0,0 +1,56 @@
+package exceptions;
+
+/**
+ * Exception thrown when there is an error related to process operations in the simulator.
+ * This includes process creation, communication, or state management errors.
+ *
+ * @author Paul C. Buetow
+ */
+public class VSProcessException extends VSSimulatorException {
+ /** The serial version uid */
+ private static final long serialVersionUID = 1L;
+
+ private final int processId;
+
+ /**
+ * Constructs a new process exception with the specified detail message.
+ *
+ * @param message the detail message
+ */
+ public VSProcessException(String message) {
+ super(message);
+ this.processId = -1;
+ }
+
+ /**
+ * Constructs a new process exception for a specific process.
+ *
+ * @param processId the ID of the process that encountered the error
+ * @param message the detail message
+ */
+ public VSProcessException(int processId, String message) {
+ super(String.format("Process %d: %s", processId, message));
+ this.processId = processId;
+ }
+
+ /**
+ * Constructs a new process exception with the specified detail message and cause.
+ *
+ * @param processId the ID of the process that encountered the error
+ * @param message the detail message
+ * @param cause the cause
+ */
+ public VSProcessException(int processId, String message, Throwable cause) {
+ super(String.format("Process %d: %s", processId, message), cause);
+ this.processId = processId;
+ }
+
+ /**
+ * Gets the process ID associated with this exception.
+ *
+ * @return the process ID, or -1 if not specific to a process
+ */
+ public int getProcessId() {
+ return processId;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/exceptions/VSProtocolException.java b/src/main/java/exceptions/VSProtocolException.java
new file mode 100644
index 0000000..3af22f1
--- /dev/null
+++ b/src/main/java/exceptions/VSProtocolException.java
@@ -0,0 +1,56 @@
+package exceptions;
+
+/**
+ * Exception thrown when there is an error in protocol execution or initialization.
+ * This includes protocol misconfiguration, invalid state transitions, or communication errors.
+ *
+ * @author Paul C. Buetow
+ */
+public class VSProtocolException extends VSSimulatorException {
+ /** The serial version uid */
+ private static final long serialVersionUID = 1L;
+
+ private final String protocolName;
+
+ /**
+ * Constructs a new protocol exception with the specified detail message.
+ *
+ * @param message the detail message
+ */
+ public VSProtocolException(String message) {
+ super(message);
+ this.protocolName = null;
+ }
+
+ /**
+ * Constructs a new protocol exception for a specific protocol.
+ *
+ * @param protocolName the name of the protocol that encountered the error
+ * @param message the detail message
+ */
+ public VSProtocolException(String protocolName, String message) {
+ super(String.format("Protocol '%s': %s", protocolName, message));
+ this.protocolName = protocolName;
+ }
+
+ /**
+ * Constructs a new protocol exception with the specified detail message and cause.
+ *
+ * @param protocolName the name of the protocol that encountered the error
+ * @param message the detail message
+ * @param cause the cause
+ */
+ public VSProtocolException(String protocolName, String message, Throwable cause) {
+ super(String.format("Protocol '%s': %s", protocolName, message), cause);
+ this.protocolName = protocolName;
+ }
+
+ /**
+ * Gets the protocol name associated with this exception.
+ *
+ * @return the protocol name, or null if not specific to a protocol
+ */
+ public String getProtocolName() {
+ return protocolName;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/exceptions/VSSerializationException.java b/src/main/java/exceptions/VSSerializationException.java
new file mode 100644
index 0000000..62594ef
--- /dev/null
+++ b/src/main/java/exceptions/VSSerializationException.java
@@ -0,0 +1,42 @@
+package exceptions;
+
+/**
+ * Exception thrown when serialization or deserialization of simulator objects fails.
+ * This is used when saving or loading simulation states.
+ *
+ * @author Paul C. Buetow
+ */
+public class VSSerializationException extends VSSimulatorException {
+ /** The serial version uid */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs a new serialization exception with the specified detail message.
+ *
+ * @param message the detail message
+ */
+ public VSSerializationException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new serialization exception with the specified detail message and cause.
+ *
+ * @param message the detail message
+ * @param cause the cause (typically an IOException)
+ */
+ public VSSerializationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructs a new serialization exception for a specific object type.
+ *
+ * @param objectType the type of object that failed to serialize
+ * @param operation either "serialize" or "deserialize"
+ * @param cause the underlying cause
+ */
+ public VSSerializationException(String objectType, String operation, Throwable cause) {
+ super(String.format("Failed to %s object of type: %s", operation, objectType), cause);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/exceptions/VSSimulatorException.java b/src/main/java/exceptions/VSSimulatorException.java
new file mode 100644
index 0000000..2ce8b3a
--- /dev/null
+++ b/src/main/java/exceptions/VSSimulatorException.java
@@ -0,0 +1,47 @@
+package exceptions;
+
+/**
+ * Base exception class for all DS-Sim simulator exceptions.
+ * This provides a common base for all custom exceptions in the application.
+ *
+ * @author Paul C. Buetow
+ */
+public class VSSimulatorException extends Exception {
+ /** The serial version uid */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs a new simulator exception with null as its detail message.
+ */
+ public VSSimulatorException() {
+ super();
+ }
+
+ /**
+ * Constructs a new simulator exception with the specified detail message.
+ *
+ * @param message the detail message
+ */
+ public VSSimulatorException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new simulator exception with the specified detail message and cause.
+ *
+ * @param message the detail message
+ * @param cause the cause
+ */
+ public VSSimulatorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructs a new simulator exception with the specified cause.
+ *
+ * @param cause the cause
+ */
+ public VSSimulatorException(Throwable cause) {
+ super(cause);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/exceptions/VSSimulatorRuntimeException.java b/src/main/java/exceptions/VSSimulatorRuntimeException.java
new file mode 100644
index 0000000..59acac9
--- /dev/null
+++ b/src/main/java/exceptions/VSSimulatorRuntimeException.java
@@ -0,0 +1,47 @@
+package exceptions;
+
+/**
+ * Base runtime exception class for DS-Sim simulator.
+ * Used for unrecoverable errors that should not be caught in normal flow.
+ *
+ * @author Paul C. Buetow
+ */
+public class VSSimulatorRuntimeException extends RuntimeException {
+ /** The serial version uid */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs a new simulator runtime exception with null as its detail message.
+ */
+ public VSSimulatorRuntimeException() {
+ super();
+ }
+
+ /**
+ * Constructs a new simulator runtime exception with the specified detail message.
+ *
+ * @param message the detail message
+ */
+ public VSSimulatorRuntimeException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new simulator runtime exception with the specified detail message and cause.
+ *
+ * @param message the detail message
+ * @param cause the cause
+ */
+ public VSSimulatorRuntimeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructs a new simulator runtime exception with the specified cause.
+ *
+ * @param cause the cause
+ */
+ public VSSimulatorRuntimeException(Throwable cause) {
+ super(cause);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/utils/VSClassLoader.java b/src/main/java/utils/VSClassLoader.java
index 37f4027..1b90e54 100644
--- a/src/main/java/utils/VSClassLoader.java
+++ b/src/main/java/utils/VSClassLoader.java
@@ -1,5 +1,8 @@
package utils;
+import exceptions.VSErrorHandler;
+import exceptions.VSSimulatorRuntimeException;
+
/**
* The class VSClassLoader. This class is used in order to create new objects
* by its classnames.
@@ -13,17 +16,26 @@ public class VSClassLoader extends ClassLoader {
* @param classname the classname
*
* @return the object
+ * @throws VSSimulatorRuntimeException if the class cannot be loaded or instantiated
*/
public Object newInstance(String classname) {
- Object object = null;
+ if (classname == null || classname.trim().isEmpty()) {
+ VSErrorHandler.warning("Attempted to load null or empty classname");
+ return null;
+ }
try {
- object = super.loadClass(classname, true).getDeclaredConstructor().newInstance();
+ return super.loadClass(classname, true).getDeclaredConstructor().newInstance();
+ } catch (ClassNotFoundException e) {
+ VSErrorHandler.handle("Class not found: " + classname, e, false);
+ return null;
+ } catch (NoSuchMethodException e) {
+ VSErrorHandler.handle("No default constructor for class: " + classname, e, false);
+ return null;
} catch (Exception e) {
- System.out.println(e + "; Classname " + classname);
+ VSErrorHandler.handle("Failed to instantiate class: " + classname, e, false);
+ return null;
}
-
- return object;
}
}