diff --git a/src/main/java/org/hobbit/core/Commands.java b/src/main/java/org/hobbit/core/Commands.java
index 87c395f..372aff7 100644
--- a/src/main/java/org/hobbit/core/Commands.java
+++ b/src/main/java/org/hobbit/core/Commands.java
@@ -33,8 +33,8 @@ private Commands() {
*/
public static final byte SYSTEM_READY_SIGNAL = 1;
/**
- * The signal sent by the benchmark controller to indicate that the
- * benchmark is ready.
+ * The signal sent by the benchmark controller to indicate that the benchmark is
+ * ready.
*/
public static final byte BENCHMARK_READY_SIGNAL = 2;
/**
@@ -64,11 +64,9 @@ private Commands() {
public static final byte BENCHMARK_FINISHED_SIGNAL = 11;
/**
- * Command used to ask a docker managing component to start a certain
- * container.
+ * Command used to ask a docker managing component to start a certain container.
*
- * The command is followed by a String containing the following JSON data:
- *
+ * The command is followed by a String containing the following JSON data:
*
* {
"image": "image-to-run",
"type": "system|benchmark",
"parent":"parent-container-id"
}
*
@@ -76,11 +74,9 @@ private Commands() {
*/
public static final byte DOCKER_CONTAINER_START = 12;
/**
- * Command used to ask a docker managing component to stop a certain
- * container.
+ * Command used to ask a docker managing component to stop a certain container.
*
- * The command is followed by a String containing the following JSON data:
- *
+ * The command is followed by a String containing the following JSON data:
*
* {
"containerId": "container-to-stop"
}
*
@@ -95,8 +91,27 @@ private Commands() {
public static final byte DOCKER_CONTAINER_TERMINATED = 16;
public static final byte START_BENCHMARK_SIGNAL = 17;
-
+
public static final byte REQUEST_SYSTEM_RESOURCES_USAGE = 18;
+ /**
+ * Command used to report an error that should be persisted as part of the
+ * experiment result data.
+ *
+ * The command is followed by a String containing the following JSON data:
+ *
+ * {
"containerId": "container reporting the error",
+ *
"errorType": "IRI of the error type (optional)",
+ *
"label": "A string that can be used as short label of an error (optional, the error type label will be used as default)"
+ *
"description": "A string that can be used as a short description of an error (optional, the error type description will be used as default)"
+ *
"details": "A string that contains details about the error, e.g., a stack trace (optional)."
+ *
}
+ *
+ *
+ *
+ * The {@link org.hobbit.core.data.ErrorData} class can be used to represent
+ * this data as Java object.
+ */
+ public static final byte REPORT_ERROR = 19;
private static final ImmutableMap ID_TO_COMMAND_NAME_MAP = generateMap();
@@ -116,7 +131,8 @@ private static ImmutableMap generateMap() {
}
/**
- * Returns the name of the command if it is defined inside the {@link Commands} class or its id as String.
+ * Returns the name of the command if it is defined inside the {@link Commands}
+ * class or its id as String.
*
* @param command the command that should be transformed into a String
* @return the name of the command or its id if the name is not known
diff --git a/src/main/java/org/hobbit/core/components/AbstractBenchmarkController.java b/src/main/java/org/hobbit/core/components/AbstractBenchmarkController.java
index 5f53737..798fcfa 100644
--- a/src/main/java/org/hobbit/core/components/AbstractBenchmarkController.java
+++ b/src/main/java/org/hobbit/core/components/AbstractBenchmarkController.java
@@ -156,24 +156,25 @@ public void init() throws Exception {
@Override
public void run() throws Exception {
- sendToCmdQueue(Commands.BENCHMARK_READY_SIGNAL);
- // wait for the start signal
- startBenchmarkMutex.acquire();
- executeBenchmark();
+ try {
+ sendToCmdQueue(Commands.BENCHMARK_READY_SIGNAL);
+ // wait for the start signal
+ startBenchmarkMutex.acquire();
+ executeBenchmark();
+ } catch (Exception e) {
+ throw reportAndWrap(e);
+ }
}
protected abstract void executeBenchmark() throws Exception;
/**
- * Creates the given number of data generators using the given image name
- * and environment variables.
+ * Creates the given number of data generators using the given image name and
+ * environment variables.
*
- * @param dataGeneratorImageName
- * name of the data generator Docker image
- * @param numberOfDataGenerators
- * number of generators that should be created
- * @param envVariables
- * environment variables for the data generators
+ * @param dataGeneratorImageName name of the data generator Docker image
+ * @param numberOfDataGenerators number of generators that should be created
+ * @param envVariables environment variables for the data generators
*/
protected void createDataGenerators(String dataGeneratorImageName, int numberOfDataGenerators,
String[] envVariables) {
@@ -181,15 +182,12 @@ protected void createDataGenerators(String dataGeneratorImageName, int numberOfD
}
/**
- * Creates the given number of task generators using the given image name
- * and environment variables.
+ * Creates the given number of task generators using the given image name and
+ * environment variables.
*
- * @param taskGeneratorImageName
- * name of the task generator Docker image
- * @param numberOfTaskGenerators
- * number of generators that should be created
- * @param envVariables
- * environment variables for the task generators
+ * @param taskGeneratorImageName name of the task generator Docker image
+ * @param numberOfTaskGenerators number of generators that should be created
+ * @param envVariables environment variables for the task generators
*/
protected void createTaskGenerators(String taskGeneratorImageName, int numberOfTaskGenerators,
String[] envVariables) {
@@ -199,14 +197,10 @@ protected void createTaskGenerators(String taskGeneratorImageName, int numberOfT
/**
* Internal method for creating generator components.
*
- * @param generatorImageName
- * name of the generator Docker image
- * @param numberOfGenerators
- * number of generators that should be created
- * @param envVariables
- * environment variables for the task generators
- * @param generatorIds
- * set of generator container names
+ * @param generatorImageName name of the generator Docker image
+ * @param numberOfGenerators number of generators that should be created
+ * @param envVariables environment variables for the task generators
+ * @param generatorIds set of generator container names
*/
private void createGenerator(String generatorImageName, int numberOfGenerators, String[] envVariables,
Set generatorIds) {
@@ -216,7 +210,8 @@ private void createGenerator(String generatorImageName, int numberOfGenerators,
// NOTE: Count only includes generators created within this method call.
variables[variables.length - 2] = Constants.GENERATOR_COUNT_KEY + "=" + numberOfGenerators;
for (int i = 0; i < numberOfGenerators; ++i) {
- // At the start generatorIds is empty, and new generators are added to it immediately.
+ // At the start generatorIds is empty, and new generators are added to it
+ // immediately.
// Current size of that set is used to make IDs for new generators.
variables[variables.length - 1] = Constants.GENERATOR_ID_KEY + "=" + generatorIds.size();
containerId = createContainer(generatorImageName, variables);
@@ -234,10 +229,9 @@ private void createGenerator(String generatorImageName, int numberOfGenerators,
* Creates the evaluate module using the given image name and environment
* variables.
*
- * @param evalModuleImageName
- * name of the evaluation module image
- * @param envVariables
- * environment variables that should be given to the module
+ * @param evalModuleImageName name of the evaluation module image
+ * @param envVariables environment variables that should be given to the
+ * module
*/
protected void createEvaluationModule(String evalModuleImageName, String[] envVariables) {
envVariables = ArrayUtils.add(envVariables, Constants.HOBBIT_EXPERIMENT_URI_KEY + "=" + experimentUri);
@@ -263,10 +257,9 @@ protected void createEvaluationStorage() {
* Creates the evaluate storage using the given image name and environment
* variables.
*
- * @param evalStorageImageName
- * name of the evaluation storage image
- * @param envVariables
- * environment variables that should be given to the component
+ * @param evalStorageImageName name of the evaluation storage image
+ * @param envVariables environment variables that should be given to the
+ * component
*/
protected void createEvaluationStorage(String evalStorageImageName, String[] envVariables) {
evalStoreContainerId = createContainer(evalStorageImageName, Constants.CONTAINER_TYPE_DATABASE, envVariables);
@@ -353,13 +346,12 @@ protected void waitForTaskGenToFinish() {
}
/**
- * This method waits for the benchmarked system to terminate or times out
- * after the given amount of time (in milliseconds).
+ * This method waits for the benchmarked system to terminate or times out after
+ * the given amount of time (in milliseconds).
*
- * @param maxWaitingTime
- * maximum waiting time in milliseconds
- * @return {@code true} if the system has been terminated or {@code false}
- * if the method timed out
+ * @param maxWaitingTime maximum waiting time in milliseconds
+ * @return {@code true} if the system has been terminated or {@code false} if
+ * the method timed out
*/
protected boolean waitForSystemToFinish(long maxWaitingTime) {
LOGGER.debug("Waiting for the benchmarked system to finish.");
@@ -422,8 +414,7 @@ protected void waitForEvalComponentsToFinish() {
* Uses the given model as result model if the result model is
* null
. Else, the two models are merged.
*
- * @param resultModel
- * the new result model
+ * @param resultModel the new result model
*/
protected void setResultModel(Model resultModel) {
try {
@@ -445,9 +436,9 @@ protected void setResultModel(Model resultModel) {
/**
* Generates a default model containing an error code and the benchmark
- * parameters if no result model has been received from the evaluation
- * module until now. If the model already has been received, the error is
- * added to the existing model.
+ * parameters if no result model has been received from the evaluation module
+ * until now. If the model already has been received, the error is added to the
+ * existing model.
*/
protected void generateErrorResultModel() {
try {
@@ -469,8 +460,7 @@ protected void generateErrorResultModel() {
}
/**
- * Adds the {@link #benchmarkParamModel} triples to the {@link #resultModel}
- * .
+ * Adds the {@link #benchmarkParamModel} triples to the {@link #resultModel} .
*/
protected void addParametersToResultModel() {
try {
@@ -480,8 +470,7 @@ protected void addParametersToResultModel() {
}
try {
Resource experimentResource = resultModel.getResource(experimentUri);
- StmtIterator iterator = benchmarkParamModel.listStatements(
- HobbitExperiments.New, null, (RDFNode) null);
+ StmtIterator iterator = benchmarkParamModel.listStatements(HobbitExperiments.New, null, (RDFNode) null);
Statement statement;
while (iterator.hasNext()) {
statement = iterator.next();
@@ -495,8 +484,7 @@ protected void addParametersToResultModel() {
/**
* Sends the result RDF model to the platform controller.
*
- * @param model
- * model containing the results
+ * @param model model containing the results
*/
protected void sendResultModel(Model model) {
try {
@@ -558,14 +546,12 @@ public void receiveCommand(byte command, byte[] data) {
}
/**
- * This method handles messages from the command bus containing the
- * information that a container terminated. It checks whether the container
- * belongs to the current benchmark and whether it has to react.
+ * This method handles messages from the command bus containing the information
+ * that a container terminated. It checks whether the container belongs to the
+ * current benchmark and whether it has to react.
*
- * @param containerName
- * the name of the terminated container
- * @param exitCode
- * the exit code of the terminated container
+ * @param containerName the name of the terminated container
+ * @param exitCode the exit code of the terminated container
*/
protected void containerTerminated(String containerName, int exitCode) {
if (dataGenContainerIds.contains(containerName)) {
diff --git a/src/main/java/org/hobbit/core/components/AbstractCommandReceivingComponent.java b/src/main/java/org/hobbit/core/components/AbstractCommandReceivingComponent.java
index 5b081c6..b1b5cec 100644
--- a/src/main/java/org/hobbit/core/components/AbstractCommandReceivingComponent.java
+++ b/src/main/java/org/hobbit/core/components/AbstractCommandReceivingComponent.java
@@ -36,8 +36,11 @@
import org.apache.commons.io.IOUtils;
import org.hobbit.core.Commands;
import org.hobbit.core.Constants;
+import org.hobbit.core.data.ErrorData;
+import org.hobbit.core.data.ReportedException;
import org.hobbit.core.data.StartCommandData;
import org.hobbit.core.data.StopCommandData;
+import org.hobbit.core.rabbit.GsonUtils;
import org.hobbit.core.rabbit.RabbitMQUtils;
import org.hobbit.core.rabbit.RabbitQueueFactory;
import org.hobbit.core.rabbit.RabbitQueueFactoryImpl;
@@ -69,20 +72,20 @@ public abstract class AbstractCommandReceivingComponent extends AbstractComponen
*/
private String responseQueueName = null;
/**
- * Mapping of RabbitMQ's correlationIDs to Future objects corresponding
- * to that RPC call.
+ * Mapping of RabbitMQ's correlationIDs to Future objects corresponding to that
+ * RPC call.
*/
private Map> responseFutures = Collections.synchronizedMap(new LinkedHashMap<>());
/**
- * Consumer of the queue that is used to receive responses for messages that
- * are sent via the command queue and for which an answer is expected.
+ * Consumer of the queue that is used to receive responses for messages that are
+ * sent via the command queue and for which an answer is expected.
*/
private Consumer responseConsumer = null;
/**
- * Factory for generating queues with which the commands are sent and
- * received. It is separated from the data connections since otherwise the
- * component can get stuck waiting for a command while the connection is
- * busy handling incoming or outgoing data.
+ * Factory for generating queues with which the commands are sent and received.
+ * It is separated from the data connections since otherwise the component can
+ * get stuck waiting for a command while the connection is busy handling
+ * incoming or outgoing data.
*/
protected RabbitQueueFactory cmdQueueFactory;
/**
@@ -107,8 +110,15 @@ public abstract class AbstractCommandReceivingComponent extends AbstractComponen
protected long cmdResponseTimeout = DEFAULT_CMD_RESPONSE_TIMEOUT;
private ExecutorService cmdThreadPool;
-
+
private boolean errorLogged = false;
+ /**
+ * Flag that is used to control whether the
+ * {@link #reportUnhandledExceptionSavely(Exception)} method should report
+ * exceptions or not. It can be set to false to provide better, more detailed
+ * reports while skipping the general reporting of unhandled exceptions.
+ */
+ protected boolean reportUnhandledExceptions = true;
/**
* Constructor.
@@ -120,7 +130,8 @@ public AbstractCommandReceivingComponent() {
/**
* Constructor.
*
- * @param execCommandsInParallel flag allowing the processing of commands in parallel
+ * @param execCommandsInParallel flag allowing the processing of commands in
+ * parallel
*/
public AbstractCommandReceivingComponent(boolean execCommandsInParallel) {
if (execCommandsInParallel) {
@@ -153,7 +164,7 @@ public void run() {
try {
handleCmd(body, properties);
} catch (Exception e) {
- if(errorLogged) {
+ if (errorLogged) {
LOGGER.error("Exception while trying to handle incoming command. {}", e.getMessage());
} else {
LOGGER.error("Exception while trying to handle incoming command.", e);
@@ -175,42 +186,32 @@ public void run() {
/**
* Sends the given command to the command queue.
*
- * @param command
- * the command that should be sent
- * @throws IOException
- * if a communication problem occurs
+ * @param command the command that should be sent
+ * @throws IOException if a communication problem occurs
*/
protected void sendToCmdQueue(byte command) throws IOException {
sendToCmdQueue(command, null);
}
/**
- * Sends the given command to the command queue with the given data
- * appended.
+ * Sends the given command to the command queue with the given data appended.
*
- * @param command
- * the command that should be sent
- * @param data
- * data that should be appended to the command
- * @throws IOException
- * if a communication problem occurs
+ * @param command the command that should be sent
+ * @param data data that should be appended to the command
+ * @throws IOException if a communication problem occurs
*/
protected void sendToCmdQueue(byte command, byte data[]) throws IOException {
sendToCmdQueue(command, data, null);
}
/**
- * Sends the given command to the command queue with the given data appended
- * and using the given properties.
+ * Sends the given command to the command queue with the given data appended and
+ * using the given properties.
*
- * @param command
- * the command that should be sent
- * @param data
- * data that should be appended to the command
- * @param props
- * properties that should be used for the message
- * @throws IOException
- * if a communication problem occurs
+ * @param command the command that should be sent
+ * @param data data that should be appended to the command
+ * @param props properties that should be used for the message
+ * @throws IOException if a communication problem occurs
*/
protected void sendToCmdQueue(byte command, byte data[], BasicProperties props) throws IOException {
byte sessionIdBytes[] = getHobbitSessionId().getBytes(Charsets.UTF_8);
@@ -232,37 +233,29 @@ protected void sendToCmdQueue(byte command, byte data[], BasicProperties props)
}
/**
- * Adds the given session id to the set of ids this component is reacting
- * to.
+ * Adds the given session id to the set of ids this component is reacting to.
*
- * @param sessionId
- * session id that should be added to the set of accepted ids.
+ * @param sessionId session id that should be added to the set of accepted ids.
*/
protected void addCommandHeaderId(String sessionId) {
acceptedCmdHeaderIds.add(sessionId);
}
/**
- * This method is called if a message is received
- * from the command queue.
+ * This method is called if a message is received from the command queue.
*
- * @param bytes
- * data from the RabbitMQ message
- * @param props
- * properties of the RabbitMQ message
+ * @param bytes data from the RabbitMQ message
+ * @param props properties of the RabbitMQ message
*/
protected void handleCmd(byte bytes[], AMQP.BasicProperties props) {
handleCmd(bytes, props.getReplyTo());
}
/**
- * This method is called if a message is received
- * from the command queue.
+ * This method is called if a message is received from the command queue.
*
- * @param bytes
- * data from the RabbitMQ message
- * @param replyTo
- * name of the queue in which response is expected
+ * @param bytes data from the RabbitMQ message
+ * @param replyTo name of the queue in which response is expected
*/
protected void handleCmd(byte bytes[], String replyTo) {
ByteBuffer buffer = ByteBuffer.wrap(bytes);
@@ -281,15 +274,13 @@ protected void handleCmd(byte bytes[], String replyTo) {
}
/**
- * This method sends a {@link Commands#DOCKER_CONTAINER_START} command to
- * create and start an instance of the given image using the given
- * environment variables.
+ * This method sends a {@link Commands#DOCKER_CONTAINER_START} command to create
+ * and start an instance of the given image using the given environment
+ * variables.
*
- * @param imageName
- * the name of the image of the docker container
- * @param envVariables
- * environment variables that should be added to the created
- * container
+ * @param imageName the name of the image of the docker container
+ * @param envVariables environment variables that should be added to the created
+ * container
* @return the name of the container instance or null if an error occurred
*/
protected String createContainer(String imageName, String[] envVariables) {
@@ -297,11 +288,10 @@ protected String createContainer(String imageName, String[] envVariables) {
}
/**
- * This method extends (if needed) the array of environment variables
- * for the container with HOBBIT specific variables.
+ * This method extends (if needed) the array of environment variables for the
+ * container with HOBBIT specific variables.
*
- * @param envVariables
- * user-provided array of environment variables
+ * @param envVariables user-provided array of environment variables
* @return the extended array of environment variables
*/
protected String[] extendContainerEnvVariables(String[] envVariables) {
@@ -322,29 +312,26 @@ protected String[] extendContainerEnvVariables(String[] envVariables) {
}
/**
- * This method sends a {@link Commands#DOCKER_CONTAINER_START} command to
- * create and start an instance of the given image using the given
- * environment variables.
+ * This method sends a {@link Commands#DOCKER_CONTAINER_START} command to create
+ * and start an instance of the given image using the given environment
+ * variables.
*
*
* Note that the containerType parameter should have one of the following
* values.
*
- * - {@link Constants#CONTAINER_TYPE_BENCHMARK} if this container is part
- * of a benchmark.
- * - {@link Constants#CONTAINER_TYPE_DATABASE} if this container is part
- * of a benchmark but should be located on a storage node.
- * - {@link Constants#CONTAINER_TYPE_SYSTEM} if this container is part of
- * a benchmarked system.
+ * - {@link Constants#CONTAINER_TYPE_BENCHMARK} if this container is part of a
+ * benchmark.
+ * - {@link Constants#CONTAINER_TYPE_DATABASE} if this container is part of a
+ * benchmark but should be located on a storage node.
+ * - {@link Constants#CONTAINER_TYPE_SYSTEM} if this container is part of a
+ * benchmarked system.
*
*
- * @param imageName
- * the name of the image of the docker container
- * @param containerType
- * the type of the container
- * @param envVariables
- * environment variables that should be added to the created
- * container
+ * @param imageName the name of the image of the docker container
+ * @param containerType the type of the container
+ * @param envVariables environment variables that should be added to the
+ * created container
* @return the name of the container instance or null if an error occurred
*/
protected String createContainer(String imageName, String containerType, String[] envVariables) {
@@ -352,34 +339,32 @@ protected String createContainer(String imageName, String containerType, String[
}
/**
- * This method sends a {@link Commands#DOCKER_CONTAINER_START} command to
- * create and start an instance of the given image using the given
- * environment variables.
+ * This method sends a {@link Commands#DOCKER_CONTAINER_START} command to create
+ * and start an instance of the given image using the given environment
+ * variables.
*
*
* Note that the containerType parameter should have one of the following
* values.
*
- * - {@link Constants#CONTAINER_TYPE_BENCHMARK} if this container is part
- * of a benchmark.
- * - {@link Constants#CONTAINER_TYPE_DATABASE} if this container is part
- * of a benchmark but should be located on a storage node.
- * - {@link Constants#CONTAINER_TYPE_SYSTEM} if this container is part of
- * a benchmarked system.
+ * - {@link Constants#CONTAINER_TYPE_BENCHMARK} if this container is part of a
+ * benchmark.
+ * - {@link Constants#CONTAINER_TYPE_DATABASE} if this container is part of a
+ * benchmark but should be located on a storage node.
+ * - {@link Constants#CONTAINER_TYPE_SYSTEM} if this container is part of a
+ * benchmarked system.
*
*
- * @param imageName
- * the name of the image of the docker container
- * @param containerType
- * the type of the container
- * @param envVariables
- * environment variables that should be added to the created
- * container
- * @param netAliases
- * network aliases that should be added to the created container
+ * @param imageName the name of the image of the docker container
+ * @param containerType the type of the container
+ * @param envVariables environment variables that should be added to the
+ * created container
+ * @param netAliases network aliases that should be added to the created
+ * container
* @return the name of the container instance or null if an error occurred
*/
- protected String createContainer(String imageName, String containerType, String[] envVariables, String[] netAliases) {
+ protected String createContainer(String imageName, String containerType, String[] envVariables,
+ String[] netAliases) {
try {
return createContainerAsync(imageName, containerType, envVariables, netAliases).get();
} catch (ExecutionException | InterruptedException e) {
@@ -389,64 +374,61 @@ protected String createContainer(String imageName, String containerType, String[
}
/**
- * This method sends a {@link Commands#DOCKER_CONTAINER_START} command to
- * create and start an instance of the given image using the given
- * environment variables.
+ * This method sends a {@link Commands#DOCKER_CONTAINER_START} command to create
+ * and start an instance of the given image using the given environment
+ * variables.
*
*
* Note that the containerType parameter should have one of the following
* values.
*
- * - {@link Constants#CONTAINER_TYPE_BENCHMARK} if this container is part
- * of a benchmark.
- * - {@link Constants#CONTAINER_TYPE_DATABASE} if this container is part
- * of a benchmark but should be located on a storage node.
- * - {@link Constants#CONTAINER_TYPE_SYSTEM} if this container is part of
- * a benchmarked system.
+ * - {@link Constants#CONTAINER_TYPE_BENCHMARK} if this container is part of a
+ * benchmark.
+ * - {@link Constants#CONTAINER_TYPE_DATABASE} if this container is part of a
+ * benchmark but should be located on a storage node.
+ * - {@link Constants#CONTAINER_TYPE_SYSTEM} if this container is part of a
+ * benchmarked system.
*
*
- * @param imageName
- * the name of the image of the docker container
- * @param containerType
- * the type of the container
- * @param envVariables
- * environment variables that should be added to the created
- * container
- * @return the Future object with the name of the container instance or null if an error occurred
+ * @param imageName the name of the image of the docker container
+ * @param containerType the type of the container
+ * @param envVariables environment variables that should be added to the
+ * created container
+ * @return the Future object with the name of the container instance or null if
+ * an error occurred
*/
protected Future createContainerAsync(String imageName, String containerType, String[] envVariables) {
return createContainerAsync(imageName, containerType, envVariables, null);
}
/**
- * This method sends a {@link Commands#DOCKER_CONTAINER_START} command to
- * create and start an instance of the given image using the given
- * environment variables.
+ * This method sends a {@link Commands#DOCKER_CONTAINER_START} command to create
+ * and start an instance of the given image using the given environment
+ * variables.
*
*
* Note that the containerType parameter should have one of the following
* values.
*
- * - {@link Constants#CONTAINER_TYPE_BENCHMARK} if this container is part
- * of a benchmark.
- * - {@link Constants#CONTAINER_TYPE_DATABASE} if this container is part
- * of a benchmark but should be located on a storage node.
- * - {@link Constants#CONTAINER_TYPE_SYSTEM} if this container is part of
- * a benchmarked system.
+ * - {@link Constants#CONTAINER_TYPE_BENCHMARK} if this container is part of a
+ * benchmark.
+ * - {@link Constants#CONTAINER_TYPE_DATABASE} if this container is part of a
+ * benchmark but should be located on a storage node.
+ * - {@link Constants#CONTAINER_TYPE_SYSTEM} if this container is part of a
+ * benchmarked system.
*
*
- * @param imageName
- * the name of the image of the docker container
- * @param containerType
- * the type of the container
- * @param envVariables
- * environment variables that should be added to the created
- * container
- * @param netAliases
- * network aliases that should be added to the created container
- * @return the Future object with the name of the container instance or null if an error occurred
- */
- protected Future createContainerAsync(String imageName, String containerType, String[] envVariables, String[] netAliases) {
+ * @param imageName the name of the image of the docker container
+ * @param containerType the type of the container
+ * @param envVariables environment variables that should be added to the
+ * created container
+ * @param netAliases network aliases that should be added to the created
+ * container
+ * @return the Future object with the name of the container instance or null if
+ * an error occurred
+ */
+ protected Future createContainerAsync(String imageName, String containerType, String[] envVariables,
+ String[] netAliases) {
try {
envVariables = extendContainerEnvVariables(envVariables);
@@ -458,8 +440,8 @@ protected Future createContainerAsync(String imageName, String container
responseFutures.put(correlationId, containerFuture);
}
- byte data[] = RabbitMQUtils.writeString(
- gson.toJson(new StartCommandData(imageName, containerType, containerName, envVariables, netAliases)));
+ byte data[] = GsonUtils.serializeObjectWithGson(gson,
+ new StartCommandData(imageName, containerType, containerName, envVariables, netAliases));
BasicProperties.Builder propsBuilder = new BasicProperties.Builder();
propsBuilder.deliveryMode(2);
propsBuilder.replyTo(responseQueueName);
@@ -475,14 +457,14 @@ protected Future createContainerAsync(String imageName, String container
}
/**
- * This method sends a {@link Commands#DOCKER_CONTAINER_STOP} command to
- * stop the container with the given id.
+ * This method sends a {@link Commands#DOCKER_CONTAINER_STOP} command to stop
+ * the container with the given id.
*
- * @param containerName
- * the name of the container instance that should be stopped
+ * @param containerName the name of the container instance that should be
+ * stopped
*/
protected void stopContainer(String containerName) {
- byte data[] = RabbitMQUtils.writeString(gson.toJson(new StopCommandData(containerName)));
+ byte data[] = GsonUtils.serializeObjectWithGson(gson, new StopCommandData(containerName));
try {
sendToCmdQueue(Commands.DOCKER_CONTAINER_STOP, data);
} catch (IOException e) {
@@ -494,8 +476,7 @@ protected void stopContainer(String containerName) {
* Internal method for initializing the {@link #responseQueueName} and the
* {@link #responseConsumer} if they haven't been initialized before.
*
- * @throws IOException
- * if a communication problem occurs
+ * @throws IOException if a communication problem occurs
*/
private void initResponseQueue() throws IOException {
if (responseQueueName == null) {
@@ -513,10 +494,12 @@ public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProp
if (key != null) {
future = responseFutures.remove(key);
if (future == null) {
- LOGGER.error("Received a message with correlationId ({}) not in map ({})", key, responseFutures.keySet());
+ LOGGER.error("Received a message with correlationId ({}) not in map ({})", key,
+ responseFutures.keySet());
}
} else {
- LOGGER.warn("Received a message with null correlationId. This is an error unless the other component uses an older version of HOBBIT core library.");
+ LOGGER.warn(
+ "Received a message with null correlationId. This is an error unless the other component uses an older version of HOBBIT core library.");
Iterator> iter = responseFutures.values().iterator();
if (iter.hasNext()) {
LOGGER.info("Correlating with the eldest request as a workaround.");
@@ -553,6 +536,90 @@ public void setCmdResponseTimeout(long cmdResponseTimeout) {
this.cmdResponseTimeout = cmdResponseTimeout;
}
+ /**
+ * This method sends the given error report on the command queue.
+ *
+ * @param error the data of the error that should be reported
+ * @throws IOException if sending the report fails
+ */
+ public void reportError(ErrorData error) throws IOException {
+ sendToCmdQueue(Commands.REPORT_ERROR, GsonUtils.serializeObjectWithGson(gson, error));
+ }
+
+ /**
+ * This method sends the given error report on the command queue without
+ * throwing an exception. This can be helpful in situations, in which the
+ * reported error may lead to a crash of the system and the reporting mechanism
+ * itself may not work anymore.
+ *
+ * @param error the data of the error that should be reported
+ */
+ public void reportErrorSavely(ErrorData error) {
+ try {
+ reportError(error);
+ } catch (IOException e) {
+ LOGGER.error("Error while reporting an error.", e);
+ }
+ }
+
+ /**
+ * This method sends a report about the given exception. The container name is
+ * derived from the configuration and the error report is generated based on the
+ * data provided by the exception. Note that instances of
+ * {@link ReportedException} are not reported.
+ *
+ * @param e the severe exception that should be reported as error
+ */
+ public void reportExceptionSavely(Exception e) {
+ if (!(e instanceof ReportedException)) {
+ reportErrorSavely(ErrorData.createFromException(e,
+ configuration.getString(Constants.CONTAINER_NAME_KEY, (String) null)));
+ }
+ }
+
+ /**
+ * This method sends a report about the given unhandled exception using the
+ * {@link #reportExceptionSavely(Exception)} method unless the flag
+ * {@link #reportUnhandledExceptions} is set to {@code false}.
+ *
+ * @param e the severe exception that should be reported as error
+ */
+ public void reportUnhandledExceptionSavely(Exception e) {
+ if (reportUnhandledExceptions) {
+ reportExceptionSavely(e);
+ }
+ }
+
+ /**
+ * Report the given unhandled exception using
+ * {@link #reportUnhandledExceptionSavely(Exception)} and return a new instance
+ * of {@link ReportedException} that can be thrown.
+ *
+ * @param e the severe exception that should be reported as error
+ * @return the new exception instance that can be thrown
+ */
+ public Exception reportAndWrap(Exception e) {
+ if (e instanceof ReportedException) {
+ return e;
+ }
+ reportExceptionSavely(e);
+ return new ReportedException(e);
+ }
+
+ /**
+ * @return the reportUnhandledExceptions
+ */
+ public boolean isReportUnhandledExceptions() {
+ return reportUnhandledExceptions;
+ }
+
+ /**
+ * @param reportUnhandledExceptions the reportUnhandledExceptions to set
+ */
+ public void setReportUnhandledExceptions(boolean reportUnhandledExceptions) {
+ this.reportUnhandledExceptions = reportUnhandledExceptions;
+ }
+
@Override
public void close() throws IOException {
if (cmdChannel != null) {
diff --git a/src/main/java/org/hobbit/core/components/AbstractDataGenerator.java b/src/main/java/org/hobbit/core/components/AbstractDataGenerator.java
index ddee396..feae186 100644
--- a/src/main/java/org/hobbit/core/components/AbstractDataGenerator.java
+++ b/src/main/java/org/hobbit/core/components/AbstractDataGenerator.java
@@ -36,7 +36,7 @@ public abstract class AbstractDataGenerator extends AbstractPlatformConnectorCom
public AbstractDataGenerator() {
defaultContainerType = Constants.CONTAINER_TYPE_BENCHMARK;
}
-
+
@Override
public void init() throws Exception {
super.init();
@@ -52,15 +52,19 @@ public void init() throws Exception {
@Override
public void run() throws Exception {
- sendToCmdQueue(Commands.DATA_GENERATOR_READY_SIGNAL);
- // Wait for the start message
- startDataGenMutex.acquire();
-
- generateData();
-
- // We have to wait until all messages are consumed
- sender2TaskGen.closeWhenFinished();
- sender2System.closeWhenFinished();
+ try {
+ sendToCmdQueue(Commands.DATA_GENERATOR_READY_SIGNAL);
+ // Wait for the start message
+ startDataGenMutex.acquire();
+
+ generateData();
+
+ // We have to wait until all messages are consumed
+ sender2TaskGen.closeWhenFinished();
+ sender2System.closeWhenFinished();
+ } catch (Exception e) {
+ throw reportAndWrap(e);
+ }
}
protected abstract void generateData() throws Exception;
diff --git a/src/main/java/org/hobbit/core/components/AbstractEvaluationModule.java b/src/main/java/org/hobbit/core/components/AbstractEvaluationModule.java
index 9d1f1fb..092e04b 100644
--- a/src/main/java/org/hobbit/core/components/AbstractEvaluationModule.java
+++ b/src/main/java/org/hobbit/core/components/AbstractEvaluationModule.java
@@ -66,7 +66,7 @@ public abstract class AbstractEvaluationModule extends AbstractPlatformConnector
/**
* Timeout parameter for delivery queue message poll.
*/
- private static final int QUEUEPOLLTIMEOUT=600000;
+ private static final int QUEUEPOLLTIMEOUT = 600000;
public AbstractEvaluationModule() {
defaultContainerType = Constants.CONTAINER_TYPE_BENCHMARK;
@@ -77,12 +77,12 @@ public void init() throws Exception {
super.init();
// Get the experiment URI
- experimentUri = configuration.getString(Constants.HOBBIT_EXPERIMENT_URI_KEY,LOGGER);
-
- evalModule2EvalStoreQueue = getFactoryForOutgoingDataQueues()
- .createDefaultRabbitQueue(generateSessionQueueName(Constants.EVAL_MODULE_2_EVAL_STORAGE_DEFAULT_QUEUE_NAME));
- evalStore2EvalModuleQueue = getFactoryForIncomingDataQueues()
- .createDefaultRabbitQueue(generateSessionQueueName(Constants.EVAL_STORAGE_2_EVAL_MODULE_DEFAULT_QUEUE_NAME));
+ experimentUri = configuration.getString(Constants.HOBBIT_EXPERIMENT_URI_KEY, LOGGER);
+
+ evalModule2EvalStoreQueue = getFactoryForOutgoingDataQueues().createDefaultRabbitQueue(
+ generateSessionQueueName(Constants.EVAL_MODULE_2_EVAL_STORAGE_DEFAULT_QUEUE_NAME));
+ evalStore2EvalModuleQueue = getFactoryForIncomingDataQueues().createDefaultRabbitQueue(
+ generateSessionQueueName(Constants.EVAL_STORAGE_2_EVAL_MODULE_DEFAULT_QUEUE_NAME));
consumer = new QueueingConsumer(evalStore2EvalModuleQueue.channel);
evalStore2EvalModuleQueue.channel.basicConsume(evalStore2EvalModuleQueue.name, consumer);
@@ -90,20 +90,23 @@ public void init() throws Exception {
@Override
public void run() throws Exception {
- sendToCmdQueue(Commands.EVAL_MODULE_READY_SIGNAL);
- collectResponses();
- Model model = summarizeEvaluation();
- LOGGER.info("The result model has " + model.size() + " triples.");
- sendResultModel(model);
+ try {
+ sendToCmdQueue(Commands.EVAL_MODULE_READY_SIGNAL);
+ collectResponses();
+ Model model = summarizeEvaluation();
+ LOGGER.info("The result model has " + model.size() + " triples.");
+ sendResultModel(model);
+ } catch (Exception e) {
+ throw reportAndWrap(e);
+ }
}
/**
- * This method communicates with the evaluation storage to collect all
- * response pairs. For every pair the
+ * This method communicates with the evaluation storage to collect all response
+ * pairs. For every pair the
* {@link #evaluateResponse(byte[], byte[], long, long)} method is called.
*
- * @throws Exception
- * if a communication error occurs.
+ * @throws Exception if a communication error occurs.
*/
protected void collectResponses() throws Exception {
byte[] expectedData;
@@ -119,19 +122,16 @@ protected void collectResponses() throws Exception {
// request next response pair
props = new BasicProperties.Builder().deliveryMode(2).replyTo(evalStore2EvalModuleQueue.name).build();
evalModule2EvalStoreQueue.channel.basicPublish("", evalModule2EvalStoreQueue.name, props, requestBody);
- //Wait for delivery message
+ // Wait for delivery message
Delivery delivery = consumer.getDeliveryQueue().poll(QUEUEPOLLTIMEOUT, TimeUnit.MILLISECONDS);
// parse the response
- if (delivery == null)
- {
- LOGGER.error("No Message Received after waiting for ten minutes");
- return;
+ if (delivery == null) {
+ LOGGER.error("No Message Received after waiting for ten minutes");
+ return;
}
- buffer = ByteBuffer.wrap(delivery.getBody());
-
-
-
+ buffer = ByteBuffer.wrap(delivery.getBody());
+
// if the response is empty
if (buffer.remaining() == 0) {
LOGGER.error("Got a completely empty response from the evaluation storage.");
@@ -151,26 +151,22 @@ protected void collectResponses() throws Exception {
responseReceivedTimestamp = data.length > 0 ? RabbitMQUtils.readLong(data) : 0;
receivedData = RabbitMQUtils.readByteArray(buffer);
-
evaluateResponse(expectedData, receivedData, taskSentTimestamp, responseReceivedTimestamp);
-
+
}
}
/**
* Evaluates the given response pair.
*
- * @param expectedData
- * the data that has been expected
- * @param receivedData
- * the data that has been received from the system
- * @param taskSentTimestamp
- * the time at which the task has been sent to the system
- * @param responseReceivedTimestamp
- * the time at which the response has been received from the
- * system
- * @throws Exception
- * if an error occurs during the evaluation
+ * @param expectedData the data that has been expected
+ * @param receivedData the data that has been received from the
+ * system
+ * @param taskSentTimestamp the time at which the task has been sent to
+ * the system
+ * @param responseReceivedTimestamp the time at which the response has been
+ * received from the system
+ * @throws Exception if an error occurs during the evaluation
*/
protected abstract void evaluateResponse(byte[] expectedData, byte[] receivedData, long taskSentTimestamp,
long responseReceivedTimestamp) throws Exception;
@@ -180,18 +176,15 @@ protected abstract void evaluateResponse(byte[] expectedData, byte[] receivedDat
* evaluation results.
*
* @return an RDF model containing the evaluation results
- * @throws Exception
- * if a sever error occurs
+ * @throws Exception if a sever error occurs
*/
protected abstract Model summarizeEvaluation() throws Exception;
/**
* Sends the model to the benchmark controller.
*
- * @param model
- * the model that should be sent
- * @throws IOException
- * if an error occurs during the commmunication
+ * @param model the model that should be sent
+ * @throws IOException if an error occurs during the commmunication
*/
private void sendResultModel(Model model) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
diff --git a/src/main/java/org/hobbit/core/components/AbstractSystemAdapter.java b/src/main/java/org/hobbit/core/components/AbstractSystemAdapter.java
index 1b0d69e..860ccd9 100644
--- a/src/main/java/org/hobbit/core/components/AbstractSystemAdapter.java
+++ b/src/main/java/org/hobbit/core/components/AbstractSystemAdapter.java
@@ -98,9 +98,9 @@ public AbstractSystemAdapter() {
/**
* Constructor setting the maximum number of messages processed in parallel.
*
- * @param maxParallelProcessedMsgs
- * The maximum number of incoming messages of a single queue that are
- * processed in parallel. Additional messages have to wait.
+ * @param maxParallelProcessedMsgs The maximum number of incoming messages of a
+ * single queue that are processed in parallel.
+ * Additional messages have to wait.
*/
public AbstractSystemAdapter(int maxParallelProcessedMsgs) {
this.maxParallelProcessedMsgs = maxParallelProcessedMsgs;
@@ -113,7 +113,7 @@ public void init() throws Exception {
// Get the benchmark parameter model
systemParamModel = configuration.getModel(Constants.SYSTEM_PARAMETERS_MODEL_KEY,
- () -> ModelFactory.createDefaultModel(), LOGGER);
+ () -> ModelFactory.createDefaultModel(), LOGGER);
dataGenReceiver = DataReceiverImpl.builder().maxParallelProcessedMsgs(maxParallelProcessedMsgs)
.queue(incomingDataQueueFactory, generateSessionQueueName(Constants.DATA_GEN_2_SYSTEM_QUEUE_NAME))
@@ -142,22 +142,26 @@ public void handleData(byte[] data) {
@Override
public void run() throws Exception {
- sendToCmdQueue(Commands.SYSTEM_READY_SIGNAL);
-
- terminateMutex.acquire();
- // Check whether the system should abort
try {
- causeMutex.acquire();
- if (cause != null) {
- throw cause;
+ sendToCmdQueue(Commands.SYSTEM_READY_SIGNAL);
+
+ terminateMutex.acquire();
+ // Check whether the system should abort
+ try {
+ causeMutex.acquire();
+ if (cause != null) {
+ throw cause;
+ }
+ causeMutex.release();
+ } catch (InterruptedException e) {
+ LOGGER.error("Interrupted while waiting to set the termination cause.");
}
- causeMutex.release();
- } catch (InterruptedException e) {
- LOGGER.error("Interrupted while waiting to set the termination cause.");
+ // Close receivers as soon as all messages have been received
+ dataGenReceiver.closeWhenFinished();
+ taskGenReceiver.closeWhenFinished();
+ } catch (Exception e) {
+ throw reportAndWrap(e);
}
- // Close receivers as soon as all messages have been received
- dataGenReceiver.closeWhenFinished();
- taskGenReceiver.closeWhenFinished();
}
@Override
@@ -173,12 +177,9 @@ public void receiveCommand(byte command, byte[] data) {
* This method sends the given result data for the task with the given task id
* to the evaluation storage.
*
- * @param taskIdString
- * the id of the task
- * @param data
- * the data of the task
- * @throws IOException
- * if there is an error during the sending
+ * @param taskIdString the id of the task
+ * @param data the data of the task
+ * @throws IOException if there is an error during the sending
*/
protected void sendResultToEvalStorage(String taskIdString, byte[] data) throws IOException {
byte[] taskIdBytes = taskIdString.getBytes(Charsets.UTF_8);
@@ -198,9 +199,8 @@ protected void sendResultToEvalStorage(String taskIdString, byte[] data) throws
* given, it will be thrown causing an abortion from the main thread instead of
* a normal termination.
*
- * @param cause
- * the cause for an abortion of the process or {code null} if the
- * component should terminate in a normal way.
+ * @param cause the cause for an abortion of the process or {code null} if the
+ * component should terminate in a normal way.
*/
protected synchronized void terminate(Exception cause) {
if (cause != null) {
diff --git a/src/main/java/org/hobbit/core/components/AbstractTaskGenerator.java b/src/main/java/org/hobbit/core/components/AbstractTaskGenerator.java
index e614401..0c20b0a 100644
--- a/src/main/java/org/hobbit/core/components/AbstractTaskGenerator.java
+++ b/src/main/java/org/hobbit/core/components/AbstractTaskGenerator.java
@@ -32,7 +32,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
/**
* This abstract class implements basic functions that can be used to implement
* a task generator.
@@ -110,21 +109,21 @@ public AbstractTaskGenerator() {
* maxParallelProcessedMsgs=1
leads to the usage of a
* {@link QueueingConsumer}.
*
- * @param maxParallelProcessedMsgs
- * the number of messaegs that are processed in parallel
+ * @param maxParallelProcessedMsgs the number of messaegs that are processed in
+ * parallel
*/
public AbstractTaskGenerator(int maxParallelProcessedMsgs) {
this.maxParallelProcessedMsgs = maxParallelProcessedMsgs;
defaultContainerType = Constants.CONTAINER_TYPE_BENCHMARK;
}
- @Override
+ @Override
public void init() throws Exception {
super.init();
- generatorId = configuration.getInt(Constants.GENERATOR_ID_KEY,LOGGER);
+ generatorId = configuration.getInt(Constants.GENERATOR_ID_KEY, LOGGER);
nextTaskId = generatorId;
- numberOfGenerators = configuration.getInt(Constants.GENERATOR_COUNT_KEY,LOGGER);
+ numberOfGenerators = configuration.getInt(Constants.GENERATOR_COUNT_KEY, LOGGER);
sender2System = DataSenderImpl.builder().queue(getFactoryForOutgoingDataQueues(),
generateSessionQueueName(Constants.TASK_GEN_2_SYSTEM_QUEUE_NAME)).build();
@@ -142,19 +141,22 @@ public void handleData(byte[] data) {
@Override
public void run() throws Exception {
- sendToCmdQueue(Commands.TASK_GENERATOR_READY_SIGNAL);
- // Wait for the start message
- startTaskGenMutex.acquire();
- currentlyProcessedMessages.release(maxParallelProcessedMsgs);
- // Wait for message to terminate
-
- terminateMutex.acquire();
- dataGenReceiver.closeWhenFinished();
- // make sure that all messages have been delivered (otherwise they might
- // be lost)
- sender2System.closeWhenFinished();
- sender2EvalStore.closeWhenFinished();
-
+ try {
+ sendToCmdQueue(Commands.TASK_GENERATOR_READY_SIGNAL);
+ // Wait for the start message
+ startTaskGenMutex.acquire();
+ currentlyProcessedMessages.release(maxParallelProcessedMsgs);
+ // Wait for message to terminate
+
+ terminateMutex.acquire();
+ dataGenReceiver.closeWhenFinished();
+ // make sure that all messages have been delivered (otherwise they might
+ // be lost)
+ sender2System.closeWhenFinished();
+ sender2EvalStore.closeWhenFinished();
+ } catch (Exception e) {
+ throw reportAndWrap(e);
+ }
}
@Override
@@ -164,9 +166,7 @@ public void receiveGeneratedData(byte[] data) {
generateTask(data);
} catch (Exception e) {
LOGGER.error("Exception while generating task.", e);
- }
- finally
- {
+ } finally {
currentlyProcessedMessages.release(1);
}
}
@@ -176,10 +176,8 @@ public void receiveGeneratedData(byte[] data) {
* timestamp of the moment at which the message has been sent to the system and
* sends it together with the expected response to the evaluation storage.
*
- * @param data
- * incoming data generated by a data generator
- * @throws Exception
- * if a sever error occurred
+ * @param data incoming data generated by a data generator
+ * @throws Exception if a sever error occurred
*/
protected abstract void generateTask(byte[] data) throws Exception;
@@ -212,15 +210,11 @@ public void receiveCommand(byte command, byte[] data) {
* This method sends the given data and the given timestamp of the task with the
* given task id to the evaluation storage.
*
- * @param taskIdString
- * the id of the task
- * @param timestamp
- * the timestamp of the moment in which the task has been sent to the
- * system
- * @param data
- * the expected response for the task with the given id
- * @throws IOException
- * if there is an error during the sending
+ * @param taskIdString the id of the task
+ * @param timestamp the timestamp of the moment in which the task has been
+ * sent to the system
+ * @param data the expected response for the task with the given id
+ * @throws IOException if there is an error during the sending
*/
protected void sendTaskToEvalStorage(String taskIdString, long timestamp, byte[] data) throws IOException {
sender2EvalStore.sendData(RabbitMQUtils.writeByteArrays(null,
@@ -230,12 +224,9 @@ protected void sendTaskToEvalStorage(String taskIdString, long timestamp, byte[]
/**
* Sends the given task with the given task id and data to the system.
*
- * @param taskIdString
- * the id of the task
- * @param data
- * the data of the task
- * @throws IOException
- * if there is an error during the sending
+ * @param taskIdString the id of the task
+ * @param data the data of the task
+ * @throws IOException if there is an error during the sending
*/
protected void sendTaskToSystemAdapter(String taskIdString, byte[] data) throws IOException {
sender2System.sendData(
diff --git a/src/main/java/org/hobbit/core/data/ErrorData.java b/src/main/java/org/hobbit/core/data/ErrorData.java
index 7c5b5bd..9a1dd85 100644
--- a/src/main/java/org/hobbit/core/data/ErrorData.java
+++ b/src/main/java/org/hobbit/core/data/ErrorData.java
@@ -1,5 +1,10 @@
package org.hobbit.core.data;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.hobbit.vocab.HobbitErrors;
+
/**
* A simple data structure that represents errors that components can report.
*
@@ -11,7 +16,7 @@ public class ErrorData {
/**
* ID of the container reporting the error.
*/
- protected String containerId;
+ protected String containerName;
/**
* IRI of the error type (optional).
*/
@@ -36,14 +41,14 @@ public class ErrorData {
* @return the containerId
*/
public String getContainerId() {
- return containerId;
+ return containerName;
}
/**
* @param containerId the containerId to set
*/
public void setContainerId(String containerId) {
- this.containerId = containerId;
+ this.containerName = containerId;
}
/**
@@ -102,4 +107,82 @@ public void setDetails(String details) {
this.details = details;
}
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((containerName == null) ? 0 : containerName.hashCode());
+ result = prime * result + ((description == null) ? 0 : description.hashCode());
+ result = prime * result + ((details == null) ? 0 : details.hashCode());
+ result = prime * result + ((errorType == null) ? 0 : errorType.hashCode());
+ result = prime * result + ((label == null) ? 0 : label.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ErrorData other = (ErrorData) obj;
+ if (containerName == null) {
+ if (other.containerName != null)
+ return false;
+ } else if (!containerName.equals(other.containerName))
+ return false;
+ if (description == null) {
+ if (other.description != null)
+ return false;
+ } else if (!description.equals(other.description))
+ return false;
+ if (details == null) {
+ if (other.details != null)
+ return false;
+ } else if (!details.equals(other.details))
+ return false;
+ if (errorType == null) {
+ if (other.errorType != null)
+ return false;
+ } else if (!errorType.equals(other.errorType))
+ return false;
+ if (label == null) {
+ if (other.label != null)
+ return false;
+ } else if (!label.equals(other.label))
+ return false;
+ return true;
+ }
+
+ /**
+ * Creates an instance of a {@link HobbitErrors#UnhandledException} error based
+ * on the data of the given exception.
+ *
+ * @param e the exception that should be expressed as error
+ * @param containerName the ID of the container that reports the error
+ * @return the newly created ErrorData instance or {@code null} if the given
+ * container name is {@code null}.
+ */
+ public static ErrorData createFromException(Exception e, String containerName) {
+ if (containerName == null) {
+ return null;
+ }
+ ErrorData result = new ErrorData();
+ result.containerName = containerName;
+ result.errorType = HobbitErrors.UnhandledException.getURI();
+ result.label = e.getClass().getName();
+ result.description = e.getMessage();
+ // Get the full stack trace
+ StringWriter writer = new StringWriter();
+ PrintWriter pwriter = new PrintWriter(writer);
+ e.printStackTrace(pwriter);
+ pwriter.flush();
+ result.details = writer.toString();
+ pwriter.close();
+
+ return result;
+ }
+
}
diff --git a/src/main/java/org/hobbit/core/data/ReportedException.java b/src/main/java/org/hobbit/core/data/ReportedException.java
new file mode 100644
index 0000000..85173c7
--- /dev/null
+++ b/src/main/java/org/hobbit/core/data/ReportedException.java
@@ -0,0 +1,26 @@
+package org.hobbit.core.data;
+
+/**
+ * This Exception is a wrapper of an exception that has already been reported on
+ * the command queue and, hence, doesn't have to be reported again.
+ *
+ * @author Michael Röder (michael.roeder@uni-paderborn.de)
+ *
+ */
+public class ReportedException extends Exception {
+
+ private static final long serialVersionUID = 2L;
+
+ public ReportedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+ public ReportedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ReportedException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/src/main/java/org/hobbit/core/run/ComponentStarter.java b/src/main/java/org/hobbit/core/run/ComponentStarter.java
index 2b87ea5..9d9de67 100644
--- a/src/main/java/org/hobbit/core/run/ComponentStarter.java
+++ b/src/main/java/org/hobbit/core/run/ComponentStarter.java
@@ -23,6 +23,7 @@
import org.apache.commons.configuration2.EnvironmentConfiguration;
import org.apache.commons.io.IOUtils;
import org.hobbit.core.Constants;
+import org.hobbit.core.components.AbstractCommandReceivingComponent;
import org.hobbit.core.components.Component;
import org.hobbit.utils.config.HobbitConfiguration;
import org.slf4j.Logger;
@@ -66,6 +67,7 @@ public static void main(String[] args) {
component.run();
} catch (Throwable t) {
LOGGER.error("Exception while executing component. Exiting with error code.", t);
+ reportErrorIfPossible(t);
success = false;
} finally {
closeComponent();
@@ -122,7 +124,7 @@ private static void configure(Component component) {
HobbitConfiguration configuration = new HobbitConfiguration();
configuration.addConfiguration(new EnvironmentConfiguration());
// Add more configurations if necessary
-
+
component.setConfiguration(configuration);
}
@@ -154,4 +156,11 @@ private static boolean forceTermination() {
}
return false;
}
+
+ private static void reportErrorIfPossible(Throwable t) {
+ if (component instanceof AbstractCommandReceivingComponent && t instanceof Exception) {
+ AbstractCommandReceivingComponent crComponent = (AbstractCommandReceivingComponent) component;
+ crComponent.reportUnhandledExceptionSavely((Exception) t);
+ }
+ }
}
diff --git a/src/main/java/org/hobbit/vocab/HobbitErrors.java b/src/main/java/org/hobbit/vocab/HobbitErrors.java
index bb32d1b..d014cd7 100644
--- a/src/main/java/org/hobbit/vocab/HobbitErrors.java
+++ b/src/main/java/org/hobbit/vocab/HobbitErrors.java
@@ -57,4 +57,6 @@ protected static final Property property(String local) {
public static final Resource TerminatedByUser = resource("TerminatedByUser");
public static final Resource UnexpectedError = resource("UnexpectedError");
public static final Resource ClusterNotHealthy = resource("ClusterNotHealthy");
+ public static final Resource UnhandledException = resource("UnhandledException");
+ public static final Resource UnspecifiedError = resource("UnspecifiedError");
}