Skip to content

Commit 94bd8d1

Browse files
committed
changes
Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
1 parent 5a23293 commit 94bd8d1

File tree

5 files changed

+611
-75
lines changed

5 files changed

+611
-75
lines changed

api/src/main/java/org/apache/cloudstack/api/response/ExceptionResponse.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public class ExceptionResponse extends BaseResponse {
4646

4747
@SerializedName("errortextkey")
4848
@Param(description = "the key for the text associated with this error")
49-
private String errorTextKey = "";
49+
private String errorTextKey;
5050

5151
@SerializedName("errormetadata")
5252
@Param(description = "the metadata associated with this error")

api/src/main/java/org/apache/cloudstack/context/ErrorMessageResolver.java

Lines changed: 111 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@
2222
import java.lang.reflect.InvocationTargetException;
2323
import java.nio.file.Files;
2424
import java.nio.file.Paths;
25+
import java.util.ArrayList;
2526
import java.util.Collections;
2627
import java.util.LinkedHashMap;
28+
import java.util.List;
2729
import java.util.Map;
30+
import java.util.regex.Matcher;
31+
import java.util.regex.Pattern;
2832

2933
import org.apache.cloudstack.api.Identity;
3034
import org.apache.cloudstack.api.InternalIdentity;
@@ -41,14 +45,15 @@
4145
import com.fasterxml.jackson.core.type.TypeReference;
4246
import com.fasterxml.jackson.databind.ObjectMapper;
4347

44-
public final class ErrorMessageResolver {
45-
48+
public class ErrorMessageResolver {
4649
private static final Logger LOG =
4750
LogManager.getLogger(ErrorMessageResolver.class);
4851

49-
private static final String ERROR_MESSAGES_FILENAME = "error-messages.json";
50-
private static final String ERROR_KEY_ADMIN_SUFFIX = ".admin";
51-
private static final boolean INCLUDE_METADATA_ID_IN_MESSAGE = false;
52+
protected static final String ERROR_MESSAGES_FILENAME = "error-messages.json";
53+
protected static final String ERROR_KEY_ADMIN_SUFFIX = ".admin";
54+
protected static final boolean INCLUDE_METADATA_ID_IN_MESSAGE = false;
55+
56+
private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{\\{\\s*([A-Za-z0-9_]+)\\s*\\}\\}");
5257

5358
private static final ObjectMapper MAPPER = new ObjectMapper();
5459

@@ -61,11 +66,43 @@ public final class ErrorMessageResolver {
6166
private ErrorMessageResolver() {
6267
}
6368

64-
public static String getMessage(String errorKey, Map<String, Object> metadata) {
65-
return getMessageUsingStringMap(errorKey, getStringMap(metadata));
69+
protected static List<String> getVariableNamesInErrorKey(String template) {
70+
if (template == null || template.isEmpty()) {
71+
return Collections.emptyList();
72+
}
73+
List<String> variables = new ArrayList<>();
74+
Matcher matcher = VARIABLE_PATTERN.matcher(template);
75+
while (matcher.find()) {
76+
String name = matcher.group(1);
77+
if (name != null && !name.isEmpty()) {
78+
variables.add(name);
79+
}
80+
}
81+
return variables;
82+
}
83+
84+
protected static Map<String, Object> getCombinedMetadataFromErrorTemplate(String template, Map<String, Object> metadata) {
85+
List<String> variableNames = getVariableNamesInErrorKey(template);
86+
if (variableNames.isEmpty()) {
87+
return metadata;
88+
}
89+
Map<String, Object> contextMetadata = CallContext.current().getContextStringKeyParameters();
90+
if (MapUtils.isEmpty(contextMetadata)) {
91+
return metadata;
92+
}
93+
Map<String, Object> combinedMetadata = new LinkedHashMap<>();
94+
for (String varName : variableNames) {
95+
if (contextMetadata.containsKey(varName)) {
96+
combinedMetadata.put(varName, contextMetadata.get(varName));
97+
}
98+
}
99+
if (MapUtils.isNotEmpty(metadata)) {
100+
combinedMetadata.putAll(metadata);
101+
}
102+
return combinedMetadata;
66103
}
67104

68-
private static String getTemplateForKey(String errorKey) {
105+
protected static String getTemplateForKey(String errorKey) {
69106
if (errorKey == null) {
70107
return null;
71108
}
@@ -79,15 +116,7 @@ private static String getTemplateForKey(String errorKey) {
79116
return templates.get(errorKey);
80117
}
81118

82-
private static String getMessageUsingStringMap(String errorKey, Map<String, String> metadata) {
83-
String template = getTemplateForKey(errorKey);
84-
if (template == null) {
85-
return errorKey;
86-
}
87-
return expand(template, metadata);
88-
}
89-
90-
private static Map<String, String> getStringMap(Map<String, Object> metadata) {
119+
protected static Map<String, String> getStringMap(Map<String, Object> metadata) {
91120
Map<String, String> stringMap = new LinkedHashMap<>();
92121
if (MapUtils.isNotEmpty(metadata)) {
93122
for (Map.Entry<String, Object> entry : metadata.entrySet()) {
@@ -98,16 +127,35 @@ private static Map<String, String> getStringMap(Map<String, Object> metadata) {
98127
return stringMap;
99128
}
100129

101-
private static String getMetadataObjectStringValue(Object obj) {
130+
/**
131+
* Converts a metadata object to a human-readable string for error messages.
132+
*
133+
* <p>Behavior:
134+
* <ul>
135+
* <li>If {@code obj} is {@code null}, returns {@code null}.</li>
136+
* <li>Attempts to obtain a display name by invoking one of the getters
137+
* {@code getDisplayText()}, {@code getDisplayName()}, or {@code getName()} via reflection.
138+
* If a name is found, returns it quoted as {@code 'NAME'}.</li>
139+
* <li>When the current calling account is a root admin, the returned value will include
140+
* an identifier suffix in the form {@code (ID: id, UUID: uuid)} when available.
141+
* The ID is included only if {@code INCLUDE_METADATA_ID_IN_MESSAGE} is {@code true}
142+
* and {@code obj} implements {@link InternalIdentity}. The UUID is included when
143+
* {@code obj} implements {@link org.apache.cloudstack.api.Identity}.</li>
144+
* <li>If no display name is available, returns the UUID (if {@code obj} implements
145+
* {@code Identity}); otherwise returns {@code obj.toString()}.</li>
146+
* </ul>
147+
*
148+
* <p>Reflection is used to call getters; invocation failures are silently ignored and treated as
149+
* absence of the corresponding value.
150+
*
151+
* @param obj metadata object
152+
* @return formatted metadata string suitable for inclusion in error messages, or {@code null}
153+
* if {@code obj} is {@code null}
154+
*/
155+
protected static String getMetadataObjectStringValue(Object obj) {
102156
if (obj == null) {
103157
return null;
104158
}
105-
// obj is of primitive type
106-
// String value structure should be 'NAME' (ID: id, UUID: uuid)
107-
// NAME is obtained from obj.getName() or obj.getDisplayText()
108-
// ID is obtained from obj.getId() if obj instanceof InternalIdentity and only for root admin
109-
// UUID is obtained from obj.getUuid() if obj instanceof Identity
110-
// If NAME is not available, fallback to obj.toString() then simply return UUID if available
111159
String uuid = null;
112160
if (obj instanceof Identity) {
113161
uuid = ((Identity) obj).getUuid();
@@ -156,7 +204,7 @@ private static String getMetadataObjectStringValue(Object obj) {
156204
return sb.toString();
157205
}
158206

159-
private static String invokeStringGetter(Object obj, String methodName) {
207+
protected static String invokeStringGetter(Object obj, String methodName) {
160208
try {
161209
Class<?> cls = obj.getClass();
162210
var m = cls.getMethod(methodName);
@@ -167,28 +215,7 @@ private static String invokeStringGetter(Object obj, String methodName) {
167215
}
168216
}
169217

170-
public static void updateExceptionResponse(ExceptionResponse response, CloudRuntimeException cre) {
171-
String key = cre.getMessageKey();
172-
Map<String, Object> map = cre.getMetadata();
173-
174-
if (key == null) {
175-
if (cre.getCause() instanceof InvalidParameterValueException) {
176-
key = ((InvalidParameterValueException) cre.getCause()).getMessageKey();
177-
map = ((InvalidParameterValueException) cre.getCause()).getMetadata();
178-
} else {
179-
return;
180-
}
181-
}
182-
response.setErrorTextKey(key);
183-
Map<String, String> stringMap = getStringMap(map);
184-
String message = getMessageUsingStringMap(key, stringMap);
185-
if (message != null) {
186-
response.setErrorText(message);
187-
}
188-
response.setErrorMetadata(stringMap);
189-
}
190-
191-
private static synchronized void reloadIfRequired() {
218+
protected static synchronized void reloadIfRequired() {
192219
try {
193220
// log current directory for debugging purposes
194221
LOG.debug("Current working directory: {}",
@@ -230,12 +257,8 @@ private static synchronized void reloadIfRequired() {
230257
}
231258
}
232259

233-
private static String expand(String template, Map<String, String> metadata) {
234-
Map<String, Object> allMetadata = CallContext.current().getContextStringKeyParameters();
235-
if (MapUtils.isNotEmpty(allMetadata)) {
236-
allMetadata.putAll(metadata);
237-
}
238-
if (MapUtils.isEmpty(allMetadata)) {
260+
protected static String expand(String template, Map<String, String> metadata) {
261+
if (MapUtils.isEmpty(metadata)) {
239262
return template;
240263
}
241264
String result = template;
@@ -248,4 +271,39 @@ private static String expand(String template, Map<String, String> metadata) {
248271
}
249272
return result;
250273
}
274+
275+
public static String getMessage(String errorKey, Map<String, Object> metadata) {
276+
String template = getTemplateForKey(errorKey);
277+
if (template == null) {
278+
return errorKey;
279+
}
280+
Map<String, Object> combinedMetadata = getCombinedMetadataFromErrorTemplate(template, metadata);
281+
return expand(template, getStringMap(combinedMetadata));
282+
}
283+
284+
public static void updateExceptionResponse(ExceptionResponse response, CloudRuntimeException cre) {
285+
String key = cre.getMessageKey();
286+
Map<String, Object> map = cre.getMetadata();
287+
288+
if (key == null) {
289+
Throwable cause = cre.getCause();
290+
if (!(cause instanceof InvalidParameterValueException)) {
291+
return;
292+
}
293+
InvalidParameterValueException ipve = (InvalidParameterValueException) cause;
294+
key = ipve.getMessageKey();
295+
map = ipve.getMetadata();
296+
}
297+
response.setErrorTextKey(key);
298+
String template = getTemplateForKey(key);
299+
if (template == null) {
300+
response.setErrorText(key);
301+
response.setErrorMetadata(getStringMap(map));
302+
return;
303+
}
304+
Map<String, Object> combinedMetadata = getCombinedMetadataFromErrorTemplate(template, map);
305+
Map<String, String> stringMap = getStringMap(combinedMetadata);
306+
response.setErrorText(expand(template, stringMap));
307+
response.setErrorMetadata(stringMap);
308+
}
251309
}

0 commit comments

Comments
 (0)