diff --git a/api/src/main/java/com/cloud/exception/InvalidParameterValueException.java b/api/src/main/java/com/cloud/exception/InvalidParameterValueException.java index b3dbcadd5e2..560a0a83570 100644 --- a/api/src/main/java/com/cloud/exception/InvalidParameterValueException.java +++ b/api/src/main/java/com/cloud/exception/InvalidParameterValueException.java @@ -31,7 +31,7 @@ public class InvalidParameterValueException extends CloudRuntimeException { } public InvalidParameterValueException(String key, Map metadata) { - super(ResponseMessageResolver.getMessage(key, metadata), key, metadata); + super(ResponseMessageResolver.resolve(key, metadata)); } } diff --git a/api/src/main/java/com/cloud/exception/PermissionDeniedException.java b/api/src/main/java/com/cloud/exception/PermissionDeniedException.java index b78504315c4..dcb0d1ee78c 100644 --- a/api/src/main/java/com/cloud/exception/PermissionDeniedException.java +++ b/api/src/main/java/com/cloud/exception/PermissionDeniedException.java @@ -17,8 +17,10 @@ package com.cloud.exception; import java.util.List; +import java.util.Map; import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.context.ResponseMessageResolver; import com.cloud.user.Account; import com.cloud.utils.SerialVersionUID; @@ -32,6 +34,10 @@ public class PermissionDeniedException extends CloudRuntimeException { super(message); } + public PermissionDeniedException(String key, Map metadata) { + super(ResponseMessageResolver.resolve(key, metadata)); + } + public PermissionDeniedException(String message, Throwable cause) { super(message, cause); } diff --git a/api/src/main/java/org/apache/cloudstack/context/ResponseMessageResolver.java b/api/src/main/java/org/apache/cloudstack/context/ResponseMessageResolver.java index 103dbf58656..9166bbfce7b 100644 --- a/api/src/main/java/org/apache/cloudstack/context/ResponseMessageResolver.java +++ b/api/src/main/java/org/apache/cloudstack/context/ResponseMessageResolver.java @@ -39,7 +39,10 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.Ternary; import com.cloud.utils.exception.CloudRuntimeException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -50,7 +53,8 @@ public class ResponseMessageResolver { protected static final String ERROR_MESSAGES_FILENAME = "error-messages.json"; protected static final String ERROR_KEY_ADMIN_SUFFIX = ".admin"; - protected static final boolean INCLUDE_METADATA_ID_IN_MESSAGE = false; + protected static final boolean USE_RESOURCE_TO_STRING_IN_METADATA = false; + protected static final boolean INCLUDE_RESOURCE_ID_FOR_ADMINS_IN_METADATA = true; private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{\\{\\s*([A-Za-z0-9_]+)\\s*\\}\\}"); @@ -129,7 +133,10 @@ public class ResponseMessageResolver { if (MapUtils.isNotEmpty(metadata)) { for (Map.Entry entry : metadata.entrySet()) { Object value = entry.getValue(); - stringMap.put(entry.getKey(), getMetadataObjectStringValueAlt(value)); + stringMap.put(entry.getKey(), + USE_RESOURCE_TO_STRING_IN_METADATA ? + getMetadataObjectStringValueAlt(value) : + getMetadataObjectStringValue(value)); } } return stringMap; @@ -192,7 +199,7 @@ public class ResponseMessageResolver { } Long id = null; - if (INCLUDE_METADATA_ID_IN_MESSAGE && obj instanceof InternalIdentity) { + if (INCLUDE_RESOURCE_ID_FOR_ADMINS_IN_METADATA && obj instanceof InternalIdentity) { id = ((InternalIdentity) obj).getId(); } @@ -324,6 +331,15 @@ public class ResponseMessageResolver { return expand(template, getStringMap(combinedMetadata)); } + public static Ternary> resolve(String errorKey, Map metadata) { + String template = getTemplateForKey(errorKey); + if (template == null) { + return new Ternary<>(errorKey, errorKey, metadata); + } + Map combinedMetadata = getCombinedMetadataFromErrorTemplate(template, metadata); + return new Ternary<>(expand(template, getStringMap(combinedMetadata)), errorKey, combinedMetadata); + } + public static void updateExceptionResponse(ExceptionResponse response, CloudRuntimeException cre) { String key = cre.getMessageKey(); Map map = cre.getMetadata(); @@ -352,4 +368,32 @@ public class ResponseMessageResolver { response.setErrorText(expand(template, stringMap)); response.setErrorMetadata(stringMap); } + + public static InvalidParameterValueException invalidParameterValueException(String errorKey, Map metadata) { + return new InvalidParameterValueException(errorKey, metadata); + } + + public static InvalidParameterValueException invalidParameterValueException(String errorKey) { + return invalidParameterValueException(errorKey, Collections.emptyMap()); + } + + public static PermissionDeniedException permissionDeniedException(String errorKey, Map metadata) { + return new PermissionDeniedException(errorKey, metadata); + } + + public static PermissionDeniedException permissionDeniedException(String errorKey) { + return permissionDeniedException(errorKey, Collections.emptyMap()); + } + + public static CloudRuntimeException cloudRuntimeException(String errorKey, Map metadata) { + return new CloudRuntimeException(resolve(errorKey, metadata)); + } + + public static CloudRuntimeException cloudRuntimeException(String errorKey) { + return new CloudRuntimeException(resolve(errorKey, Collections.emptyMap())); + } + + public static CloudRuntimeException cloudRuntimeException(String errorKey, Map metadata, Throwable th) { + return new CloudRuntimeException(resolve(errorKey, metadata), th); + } } diff --git a/api/src/test/java/org/apache/cloudstack/context/ResponseMessageResolverTest.java b/api/src/test/java/org/apache/cloudstack/context/ResponseMessageResolverTest.java index 545b34350b0..84bf7db00bf 100644 --- a/api/src/test/java/org/apache/cloudstack/context/ResponseMessageResolverTest.java +++ b/api/src/test/java/org/apache/cloudstack/context/ResponseMessageResolverTest.java @@ -230,12 +230,12 @@ public class ResponseMessageResolverTest { public void getMetadataObjectStringValue_shouldIncludeIdAndUuidForRootAdmin() { DataCenter internalIdentityMock = Mockito.mock(DataCenter.class); when(internalIdentityMock.getUuid()).thenReturn("uuid-5678"); - if (ResponseMessageResolver.INCLUDE_METADATA_ID_IN_MESSAGE) { + if (ResponseMessageResolver.INCLUDE_METADATA_ID_FOR_ADMINS_IN_MESSAGE) { when(internalIdentityMock.getId()).thenReturn(42L); } when(internalIdentityMock.getName()).thenReturn("AdminName"); when(CallContext.current().isCallingAccountRootAdmin()).thenReturn(true); - String expected = String.format("'AdminName' (%sUUID: uuid-5678)", ResponseMessageResolver.INCLUDE_METADATA_ID_IN_MESSAGE ? "ID: 42, " : ""); + String expected = String.format("'AdminName' (%sUUID: uuid-5678)", ResponseMessageResolver.INCLUDE_METADATA_ID_FOR_ADMINS_IN_MESSAGE ? "ID: 42, " : ""); Assert.assertEquals(expected, ResponseMessageResolver.getMetadataObjectStringValue(internalIdentityMock)); } diff --git a/utils/src/main/java/com/cloud/utils/exception/CloudRuntimeException.java b/utils/src/main/java/com/cloud/utils/exception/CloudRuntimeException.java index 5350848944e..bbd23240bd2 100644 --- a/utils/src/main/java/com/cloud/utils/exception/CloudRuntimeException.java +++ b/utils/src/main/java/com/cloud/utils/exception/CloudRuntimeException.java @@ -28,6 +28,7 @@ import java.util.Map; import com.cloud.utils.Pair; import com.cloud.utils.SerialVersionUID; +import com.cloud.utils.Ternary; /** * wrap exceptions that you know there's no point in dealing with. @@ -63,6 +64,20 @@ public class CloudRuntimeException extends RuntimeException implements ErrorCont setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName())); } + public CloudRuntimeException(Ternary> messageKeyAndMetadata) { + super(messageKeyAndMetadata.first()); + this.messageKey = messageKeyAndMetadata.second(); + this.metadata = messageKeyAndMetadata.third(); + setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName())); + } + + public CloudRuntimeException(Ternary> messageKeyAndMetadata, Throwable th) { + super(messageKeyAndMetadata.first(), th); + this.messageKey = messageKeyAndMetadata.second(); + this.metadata = messageKeyAndMetadata.third(); + setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName())); + } + protected CloudRuntimeException() { super();