First cut at REST errors
- Added a new client-facing class to hold a formatted error
- Renamed the existing RestError to be RestErrorCatalogEntry
- Modified the Intent POST operation to return an actual error if the
POST can't parse the inbound object
Change-Id: I05d8919626f1a9350262d4aee7919c0fc8a4fa09
diff --git a/src/main/java/net/onrc/onos/api/rest/RestError.java b/src/main/java/net/onrc/onos/api/rest/RestError.java
index 8b958de..dd61d26 100644
--- a/src/main/java/net/onrc/onos/api/rest/RestError.java
+++ b/src/main/java/net/onrc/onos/api/rest/RestError.java
@@ -1,60 +1,84 @@
package net.onrc.onos.api.rest;
/**
- * Describes a REST error.
- * code is a unique identifier for the error.
- * summary is indended to be a short description of what happened.
- * description is a long description of the problem, and can be formatted using
- * variable replacement. Variable placeholders are indicated with the string
- * "{}" in the description.
- * Objects of this class are immutable.
+ * Object to represent an instance of a particular REST error. The error
+ * contains a formatted description which has information about the particular
+ * occurence. Objects of this type are passed back to REST callers if an error
+ * is encountered.
*/
-
public final class RestError {
private final RestErrorCodes.RestErrorCode code;
private final String summary;
- private final String description;
+ private final String formattedDescription;
/**
- * Constructs a new RestError object from a code, summary and description.
- *
- * @param newCode code for the new error
- * @param newSummary short summary for the new error
- * @param newDescription formatable description for the new error
+ * Hidden default constructor to force usage of the factory method.
*/
- public RestError(final RestErrorCodes.RestErrorCode newCode,
- final String newSummary,
- final String newDescription) {
- code = newCode;
- summary = newSummary;
- description = newDescription;
+ private RestError() {
+ // This is never called, but Java requires these initializations
+ // because the members are final.
+ code = RestErrorCodes.RestErrorCode.INTENT_ALREADY_EXISTS;
+ summary = "";
+ formattedDescription = "";
}
/**
- * Gets the summary of the error.
+ * Constructor to make a new Error. Called by factory method.
*
- * @return string for the summary
+ * @param code code for the new error
+ * @param summary summary string for the new error
+ * @param formattedDescription formatted full description of the error
*/
- public String getSummary() {
- return summary;
+ private RestError(final RestErrorCodes.RestErrorCode code,
+ final String summary,
+ final String formattedDescription) {
+ this.code = code;
+ this.summary = summary;
+ this.formattedDescription = formattedDescription;
}
/**
- * Gets the unique code for this error.
+ * Fetch the code for this error.
*
- * @return unique code
+ * @return error code
*/
public RestErrorCodes.RestErrorCode getCode() {
return code;
}
/**
- * Gets the unformatted description string for the error.
+ * Fetch the summary for this error.
*
- * @return the unformatted description string.
+ * @return summary
*/
- public String getDescription() {
- return description;
+ public String getSummary() {
+ return summary;
+ }
+
+ /**
+ * Fetch the formatted descritpion for this error.
+ *
+ * @return formatted error
+ */
+ public String getFormattedDescription() {
+ return formattedDescription;
+ }
+
+ /**
+ * Creates an object to represent an instance of a REST error. The
+ * descrption is formatted for this particular instance.
+ *
+ * @param code code of the error in the catalog
+ * @param parameters list of positional parameters to use in formatting
+ * the description
+ * @return new RestError representing this intance
+ */
+ public static RestError createRestError(final RestErrorCodes.RestErrorCode code,
+ final Object... parameters) {
+ final RestErrorCatalogEntry error = RestErrorCatalog.getRestError(code);
+ final String formattedDescription =
+ RestErrorFormatter.formatErrorMessage(error, parameters);
+ return new RestError(code, error.getSummary(), formattedDescription);
}
}
diff --git a/src/main/java/net/onrc/onos/api/rest/RestErrorCatalog.java b/src/main/java/net/onrc/onos/api/rest/RestErrorCatalog.java
index d81805f..4a8a765 100644
--- a/src/main/java/net/onrc/onos/api/rest/RestErrorCatalog.java
+++ b/src/main/java/net/onrc/onos/api/rest/RestErrorCatalog.java
@@ -1,5 +1,6 @@
package net.onrc.onos.api.rest;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -17,17 +18,20 @@
* Static list of known errors. Someday this will be read in from an
* external file.
*/
- private static final RestError[] ERROR_LIST = {
- new RestError(RestErrorCodes.RestErrorCode.INTENT_NOT_FOUND,
+ private static final RestErrorCatalogEntry[] ERROR_LIST = {
+ new RestErrorCatalogEntry(RestErrorCodes.RestErrorCode.INTENT_NOT_FOUND,
"Intent not found",
"An intent with the identifier {} was not found."),
- new RestError(RestErrorCodes.RestErrorCode.INTENT_ALREADY_EXISTS,
+ new RestErrorCatalogEntry(RestErrorCodes.RestErrorCode.INTENT_ALREADY_EXISTS,
"Intent already exists",
"An intent with the identifier {} could not be created " +
"because one already exists."),
- new RestError(RestErrorCodes.RestErrorCode.INTENT_NO_PATH,
+ new RestErrorCatalogEntry(RestErrorCodes.RestErrorCode.INTENT_NO_PATH,
"No path found",
"No path found between {} and {}"),
+ new RestErrorCatalogEntry(RestErrorCodes.RestErrorCode.INTENT_INVALID,
+ "Intent invalid",
+ "The intent provided is empty or invalid"),
};
@@ -35,17 +39,19 @@
* Singleton implementation using the demand holder idiom.
*/
private static class RestErrorMapHolder {
+ private static Map<Integer, RestErrorCatalogEntry> restErrorMap = initializeRestErrorMap();
+
/**
* Load up the error map.
*
* @return REST error map
*/
- private static Map<Integer, RestError> initializeRestErrorMap() {
+ private static Map<Integer, RestErrorCatalogEntry> initializeRestErrorMap() {
restErrorMap = new HashMap<>();
- for (final RestError restError : ERROR_LIST) {
- restErrorMap.put(restError.getCode().ordinal(), restError);
+ for (final RestErrorCatalogEntry restErrorCatalogEntry : ERROR_LIST) {
+ restErrorMap.put(restErrorCatalogEntry.getCode().ordinal(), restErrorCatalogEntry);
}
- return restErrorMap;
+ return Collections.unmodifiableMap(restErrorMap);
}
/**
@@ -54,12 +60,10 @@
* @return map of the Rest Errors that was created from the known error
* list.
*/
- public static Map<Integer, RestError> getRestErrorMap() {
+ public static Map<Integer, RestErrorCatalogEntry> getRestErrorMap() {
return restErrorMap;
}
-
- private static Map<Integer, RestError> restErrorMap = initializeRestErrorMap();
}
/**
@@ -67,18 +71,18 @@
*
* @return map of possible REST errors.
*/
- public static Map<Integer, RestError> getRestErrorMap() {
+ public static Map<Integer, RestErrorCatalogEntry> getRestErrorMap() {
return RestErrorMapHolder.getRestErrorMap();
}
/**
- * Fetch the RestError for the given code.
+ * Fetch the RestErrorCatalogEntry for the given code.
*
* @param code the code for the message to look up.
* @return the REST error for the code if one exists, null if it does not
* exist.
*/
- public static RestError getRestError(final RestErrorCodes.RestErrorCode code) {
+ public static RestErrorCatalogEntry getRestError(final RestErrorCodes.RestErrorCode code) {
return getRestErrorMap().get(code.ordinal());
}
}
diff --git a/src/main/java/net/onrc/onos/api/rest/RestErrorCatalogEntry.java b/src/main/java/net/onrc/onos/api/rest/RestErrorCatalogEntry.java
new file mode 100644
index 0000000..96b3eea
--- /dev/null
+++ b/src/main/java/net/onrc/onos/api/rest/RestErrorCatalogEntry.java
@@ -0,0 +1,60 @@
+package net.onrc.onos.api.rest;
+
+/**
+ * Describes a REST error.
+ * code is a unique identifier for the error.
+ * summary is indended to be a short description of what happened.
+ * descriptionFormatString is a long description of the problem, and can be formatted using
+ * variable replacement. Variable placeholders are indicated with the string
+ * "{}" in the descriptionFormatString.
+ * Objects of this class are immutable.
+ */
+
+public final class RestErrorCatalogEntry {
+
+ private final RestErrorCodes.RestErrorCode code;
+ private final String summary;
+ private final String descriptionFormatString;
+
+ /**
+ * Constructs a new RestErrorCatalogEntry object from a code, summary and descriptionFormatString.
+ *
+ * @param newCode code for the new error
+ * @param newSummary short summary for the new error
+ * @param newDescriptionFormatString formatable description for the new error
+ */
+ public RestErrorCatalogEntry(final RestErrorCodes.RestErrorCode newCode,
+ final String newSummary,
+ final String newDescriptionFormatString) {
+ code = newCode;
+ summary = newSummary;
+ descriptionFormatString = newDescriptionFormatString;
+ }
+
+ /**
+ * Gets the summary of the error.
+ *
+ * @return string for the summary
+ */
+ public String getSummary() {
+ return summary;
+ }
+
+ /**
+ * Gets the unique code for this error.
+ *
+ * @return unique code
+ */
+ public RestErrorCodes.RestErrorCode getCode() {
+ return code;
+ }
+
+ /**
+ * Gets the unformatted description string for the error.
+ *
+ * @return the unformatted description string.
+ */
+ public String getDescriptionFormatString() {
+ return descriptionFormatString;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/api/rest/RestErrorCodes.java b/src/main/java/net/onrc/onos/api/rest/RestErrorCodes.java
index 1d34615..09626d2 100644
--- a/src/main/java/net/onrc/onos/api/rest/RestErrorCodes.java
+++ b/src/main/java/net/onrc/onos/api/rest/RestErrorCodes.java
@@ -24,7 +24,10 @@
INTENT_ALREADY_EXISTS,
/** No path available for the Intent. */
- INTENT_NO_PATH
+ INTENT_NO_PATH,
+
+ /** An object specified for an intent is invalid (parsing error). */
+ INTENT_INVALID
}
}
diff --git a/src/main/java/net/onrc/onos/api/rest/RestErrorFormatter.java b/src/main/java/net/onrc/onos/api/rest/RestErrorFormatter.java
index edc39b2..b9b2ba5 100644
--- a/src/main/java/net/onrc/onos/api/rest/RestErrorFormatter.java
+++ b/src/main/java/net/onrc/onos/api/rest/RestErrorFormatter.java
@@ -14,19 +14,19 @@
private RestErrorFormatter() { }
/**
- * Takes a RestError template and formats the description using a supplied
+ * Takes a RestErrorCatalogEntry template and formats the description using a supplied
* list of replacement parameters.
*
- * @param error the RestError to format
+ * @param error the RestErrorCatalogEntry to format
* @param parameters parameter list to use as positional parameters in the
* result string
*
* @return the String object for the formatted message.
*/
- static String formatErrorMessage(final RestError error,
+ static String formatErrorMessage(final RestErrorCatalogEntry error,
final Object... parameters) {
final FormattingTuple formattingResult =
- MessageFormatter.arrayFormat(error.getDescription(), parameters);
+ MessageFormatter.arrayFormat(error.getDescriptionFormatString(), parameters);
return formattingResult.getMessage();
}
}