This is what my Rest
endpoint looks like
@Post("json")
public List<LogProcessorExpression> addLogProcessorExpression(
final List<LogProcessorExpression> expressions) throws LPRestletException {
if (expressions == null || expressions.isEmpty()) {
return Collections.emptyList();
}
final Integer currentTenantId = Utils.getCurrentTenantId(getRequest());
return customAttributesManager.addLogProcessorExpression(currentTenantId, expressions);
}
the method it calls looks like
List<LogProcessorExpression> addLogProcessorExpression(final Integer currentTenantId,
final List<LogProcessorExpression> expressions)
throws LPRestletException {
final Map<String, LogProcessorExpression> cache = getCacheByCustomAttributeName(expressions);
try {
final List<Customattributesmetadata> cams =
getCustomAttributesMetaDataForTenant(currentTenantId);
for (final Customattributesmetadata metadata : cams) {
if (cache.containsKey(metadata.getAttributecolumnname())) {
metadata.setLogprocessorexpression(
cache.get(metadata.getAttributecolumnname()).toString());
}
metadata.save();
}
} catch (final TorqueException e) {
final String error = "Failed to update LogExpression custom attributes";
LOGGER.error(error, e);
throw new LPRestletException(error, e);
}
return expressions;
}
which calls to other methods in chain. what I realized when accessing this endpoint as
curl -v -H "Authorization:Basic Y3VyYasqrwqrjQGdtYWlsLmNvbTp0YXAzYWg=" \
-H "Content-Type:application/json" \
-d '[{"source": "ad", "attributePrefix": "ad_", "attributeName": "department"}]' \
http://172.11.041.240:8080/api/rest/msp/attributes
That it returns
{
"code" : 500
"message" : "The server encountered an unexpected condition which prevented it from fulfilling the request",
}
When I looked at the logs, I see lines as
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.shn.api.dto.LogProcessorExpression
at com.shn.api.restlet.logprocessor.CustomAttributesManager.getCacheByCustomAttributeName(CustomAttributesManager.java:55)
at com.shn.api.restlet.logprocessor.CustomAttributesManager.addLogProcessorExpression(CustomAttributesManager.java:24)
at com.shn.api.restlet.logprocessor.CustomAttributeMetadataRestlet.addLogProcessorExpression(CustomAttributeMetadataRestlet.java:44)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.restlet.resource.ServerResource.doHandle(ServerResource.java:521)
... 67 more
Question
- Why is it not casting them to List<LogProcessorExpression>
?
- What do I need to do to fix it?
UPDATE
Line 24 looks like
final Map<String, LogProcessorExpression> cache = getCacheByCustomAttributeName(expressions);
Line 44 looks like
return customAttributesManager.addLogProcessorExpression(currentTenantId, expressions);
getCacheByCustomAttributeName(expressions)
looks like
Map<String, LogProcessorExpression> getCacheByCustomAttributeName(
final List<LogProcessorExpression> expressions) {
if (expressions == null || expressions.isEmpty()) {
return Collections.emptyMap();
}
final Map<String, LogProcessorExpression> attributeByExpression = new HashMap<>();
for (final LogProcessorExpression expression : expressions) {
attributeByExpression.put(expression.getAttributeName(), expression);
}
return attributeByExpression;
}
I think restlet might not be sophisticated enough to extract the parameterized type List<LogProcessorExpression>
from your handler method's parameter list.
@Post("json")
public List<LogProcessorExpression> addLogProcessorExpression(
final List<LogProcessorExpression> expressions)
It just takes List
and presumably uses that when deserializing (with Jackson). Jackson uses LinkedHashMap
as a deserialization target type when one isn't provided.
Short of restlet implementing this better (maybe in a newer version?), a potential solution is to define a custom type
class LogProcessorExpressionList extends ArrayList<LogProcessorExpression> {}
and using that type as the parameter type
@Post("json")
public List<LogProcessorExpression> addLogProcessorExpression(
final LogProcessorExpressionList expressions)
Jackson can then extract the parameterized super type of the type LogProcessorExpressionList
, which is ArrayList<LogProcessorExpression>
, from which it can extract LogProcessorExpression
as the target element type.