Fixed unmarshalling in case of unknown types

This commit is contained in:
Luca Frosini 2021-02-11 17:24:46 +01:00
parent 44056157e1
commit 60e9de983a
3 changed files with 134 additions and 105 deletions

View File

@ -62,7 +62,7 @@ public class TypeMapper {
return json;
}
public static Type deserializeTypeDefinition(String json) throws Exception{
public static Type deserializeTypeDefinition(String json) throws Exception {
Type type = mapper.readValue(json, Type.class);
return type;
}

View File

@ -20,7 +20,6 @@ import org.gcube.com.fasterxml.jackson.databind.JavaType;
import org.gcube.com.fasterxml.jackson.databind.JsonMappingException;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
import org.gcube.com.fasterxml.jackson.databind.module.SimpleModule;
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
import org.gcube.com.fasterxml.jackson.databind.node.JsonNodeType;
@ -217,85 +216,71 @@ public abstract class ElementMapper {
return mapper.readValue(stream, clz);
}
public static JsonNode analizeJsonToReplaceType(JsonNode jsonNodeToAnalize, String typeIdToReplace) throws Exception {
boolean replaced = false;
protected static StringBuffer getError(String unknownType) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(unknownType);
stringBuffer.append(" is an unknown type. Please provide ");
stringBuffer.append(Element.SUPERCLASSES_PROPERTY);
stringBuffer.append(" property as string array to allow to instantiate the most appropriated class.");
return stringBuffer;
}
protected static JsonNode setAvailableSuperclass(JsonNode jsonNode) {
String unknownType = jsonNode.get(Element.CLASS_PROPERTY).asText();
ArrayNode arrayNode = (ArrayNode) jsonNode.get(Element.SUPERCLASSES_PROPERTY);
// TODO save original class and superclasses
String candidatedSuperClass = null;
int index = 0;
String typeId = null;
if(jsonNodeToAnalize.has(Element.CLASS_PROPERTY)) {
typeId = jsonNodeToAnalize.get(Element.CLASS_PROPERTY).asText();
}
if(typeId != null && typeIdToReplace.compareTo(typeId) == 0) {
JsonNode superClassesTreeNode = jsonNodeToAnalize.get(Element.SUPERCLASSES_PROPERTY);
if(superClassesTreeNode != null && superClassesTreeNode.isArray()) {
ArrayNode arrayNode = (ArrayNode) superClassesTreeNode;
for(int i = 0; i < arrayNode.size(); i++) {
try {
JsonNode jsonNode = arrayNode.get(i);
JsonNodeType jsonNodeType = jsonNode.getNodeType();
switch(jsonNodeType) {
case STRING:
String superClass = jsonNode.asText();
try {
Enum.valueOf(AccessType.class, superClass.toUpperCase());
// It is one of the BaseType. Looking for
// another type because the base one
continue;
} catch(Exception ex) {
// can continue discovery
}
if(knownTypes.containsKey(superClass)) {
candidatedSuperClass = superClass;
index = i;
}
break;
default:
break;
}
if(candidatedSuperClass!=null) {
break;
}
} catch(Exception ex) {
// Trying the next one
for(int i = 0; i < arrayNode.size(); i++) {
String superClass = arrayNode.get(i).asText();
if(knownTypes.containsKey(superClass)) {
candidatedSuperClass = superClass;
try {
// Checking if it is one of the base type. In some cases we need to use dummy
// implementation
AccessType accessType = Enum.valueOf(AccessType.class, superClass.toUpperCase());
// It is one of the BaseType.
// Looking if we need to set the dummy implementation class
if(accessType.getDummyImplementationClass()!=null) {
// This should not happen because the type has been assigned already to the dummy class.
candidatedSuperClass = accessType.getDummyImplementationClass().getSimpleName();
}
} catch(Exception ex) {
// can continue discovery
}
arrayNode.remove(index);
((ObjectNode) jsonNodeToAnalize).set(Element.CLASS_PROPERTY, new TextNode(candidatedSuperClass));
replaced = true;
break;
}
}
if(candidatedSuperClass!=null) {
((ObjectNode) jsonNode).set(Element.CLASS_PROPERTY, new TextNode(candidatedSuperClass));
return jsonNode;
}
if(!replaced) {
// continue to search inside the object
Iterator<String> iterator = jsonNodeToAnalize.fieldNames();
while(iterator.hasNext()) {
String fieldName = iterator.next();
JsonNode jn = jsonNodeToAnalize.get(fieldName);
if(jn.getNodeType() == JsonNodeType.OBJECT) {
try {
JsonNode newValue = analizeJsonToReplaceType(jn,typeIdToReplace);
replaced = true;
((ObjectNode) jsonNodeToAnalize).set(fieldName, newValue);
break;
}catch (Exception e) {
continue;
StringBuffer stringBuffer = getError(unknownType);
logger.trace("Unable to unmarshall {}. {}", jsonNode.toString(), stringBuffer.toString());
throw new RuntimeException(stringBuffer.toString());
}
protected static JsonNode analizeFullJson(JsonNode jsonNode) {
Iterator<String> iterator = jsonNode.fieldNames();
while(iterator.hasNext()) {
String fieldName = iterator.next();
JsonNode jn = jsonNode.get(fieldName);
if(jn.getNodeType() == JsonNodeType.OBJECT) {
if(jn.has(Element.CLASS_PROPERTY)) {
String cls = jn.get(Element.CLASS_PROPERTY).asText();
if(!knownTypes.containsKey(cls)) {
jn = setAvailableSuperclass(jn);
}
}
jn = analizeFullJson(jn);
((ObjectNode) jsonNode).set(fieldName, jn);
}
}
if(!replaced) {
throw new Exception();
}
return jsonNodeToAnalize;
return jsonNode;
}
/**
@ -311,40 +296,10 @@ public abstract class ElementMapper {
throws JsonParseException, JsonMappingException, IOException {
try {
return mapper.readValue(string, clz);
} catch(InvalidTypeIdException e) {
String typeId = e.getTypeId();
/* An initial step to support the Jackson 2.11
if(knownTypes.containsKey(typeId)) {
// problem of type is not subtype of itself
Class<? extends Element> superClass = null;
Class<?>[] interfaces = clz.getInterfaces();
for(Class<?> interfaceClass : interfaces) {
if(Element.class.isAssignableFrom(interfaceClass)) {
try {
superClass = (Class<? extends Element>) interfaceClass;
return (ISM) ElementMapper.unmarshal(superClass, string);
}catch (Exception ex) {
// Trying the next type
logger.error("", e);
}
}
}
throw e;
}
*/
} catch (JsonMappingException e) {
JsonNode jsonNode = mapper.readTree(string);
try {
jsonNode = analizeJsonToReplaceType(jsonNode, typeId);
}catch (Exception ex) {
throw e;
}
jsonNode = analizeFullJson(jsonNode);
return ElementMapper.unmarshal(clz, mapper.writeValueAsString(jsonNode));
} catch (Exception e) {
throw e;
}
}

View File

@ -4,6 +4,9 @@ import java.util.Calendar;
import java.util.UUID;
import org.gcube.informationsystem.base.reference.Element;
import org.gcube.informationsystem.model.impl.entities.DummyFacet;
import org.gcube.informationsystem.model.impl.entities.DummyResource;
import org.gcube.informationsystem.model.impl.relations.ConsistsOfImpl;
import org.gcube.informationsystem.model.reference.entities.Facet;
import org.gcube.informationsystem.model.reference.entities.Resource;
import org.gcube.informationsystem.model.reference.properties.PropagationConstraint;
@ -85,6 +88,76 @@ public class PropagationConstraintTest {
}
@Test
public void test() throws Exception {
Resource resource = new DummyResource();
Facet facet = new DummyFacet();
ConsistsOf<Resource, Facet> consistsOf = new ConsistsOfImpl<Resource, Facet>(resource, facet);
String json = ElementMapper.marshal(consistsOf);
logger.debug(json);
}
@Test
public void testOk() throws Exception {
String ok ="{\n"
+ " \"@class\": \"ConsistsOf\",\n"
+ " \"propagationConstraint\": {\n"
+ " \"@class\": \"PropagationConstraint\",\n"
+ " \"@superClasses\": [\n"
+ " \"Property\"\n"
+ " ],\n"
+ " \"add\": \"propagate\",\n"
+ " \"remove\": \"cascade\"\n"
+ " },\n"
+ " \"header\": {\n"
+ " \"@class\": \"Header\",\n"
+ " \"uuid\": \"4fbb4aeb-9755-4b8f-b997-0bf21a8f56ff\",\n"
+ " \"creator\": \"luca.frosini\",\n"
+ " \"modifiedBy\": \"luca.frosini\",\n"
+ " \"creationTime\": \"2021-02-11 15:07:46.887 +0000\",\n"
+ " \"lastUpdateTime\": \"2021-02-11 15:07:46.887 +0000\"\n"
+ " },\n"
+ " \"source\": {\n"
+ " \"@class\": \"DummyResource\",\n"
+ " \"@superClasses\": [\n"
+ " \"EService\",\n"
+ " \"Service\",\n"
+ " \"Resource\"\n"
+ " ],\n"
+ " \"header\": {\n"
+ " \"@class\": \"Header\",\n"
+ " \"uuid\": \"4a971915-ca90-48cf-9975-63ee2dd28605\",\n"
+ " \"creator\": null,\n"
+ " \"creationTime\": null,\n"
+ " \"lastUpdateTime\": null\n"
+ " }\n"
+ " },\n"
+ " \"target\": {\n"
+ " \"@class\": \"DummyFacet\",\n"
+ " \"@superClasses\": [\n"
+ " \"SoftwareFacet\",\n"
+ " \"Facet\"\n"
+ " ],\n"
+ " \"header\": {\n"
+ " \"@class\": \"Header\",\n"
+ " \"uuid\": \"3ace4bd0-e5cd-49a3-97a8-a0a9468ce6d4\",\n"
+ " \"creator\": null,\n"
+ " \"creationTime\": null,\n"
+ " \"lastUpdateTime\": null\n"
+ " },\n"
+ " \"name\": \"WhnManager\",\n"
+ " \"group\": \"VREManagement\",\n"
+ " \"version\": \"2.0.0-SNAPSHOT\",\n"
+ " \"description\": \"Web Hosting Node Service\",\n"
+ " \"qualifier\": null,\n"
+ " \"optional\": false\n"
+ " }\n"
+ "}";
@SuppressWarnings("unchecked")
ConsistsOf<Resource,Facet> consistsOf = ElementMapper.unmarshal(ConsistsOf.class, ok);
Assert.assertTrue(consistsOf instanceof ConsistsOf);
}
@Test
public void testPropagationConstraintSpecilizationInside() throws Exception {
AddConstraint addConstraint = AddConstraint.propagate;
@ -102,8 +175,9 @@ public class PropagationConstraintTest {
String hString = ElementMapper.marshal(header);
// TODO This fails
String consistsOfString = "{\"" + Element.CLASS_PROPERTY + "\":\"" + ConsistsOf.NAME + "\",\"" + ConsistsOf.PROPAGATION_CONSTRAINT_PROPERTY + "\":"+ pcString + ",\"" + ConsistsOf.HEADER_PROPERTY + "\": " + hString + ",\"" + ConsistsOf.TARGET_PROPERTY + "\":{\"@superClasses\":[\"SoftwareFacet\", \"Facet\"],\"@class\":\"MySoftwareFacet\",\"header\":"+ "{\"@class\":\"Header\",\"uuid\":\"3ace4bd0-e5cd-49a3-97a8-a0a9468ce6d4\",\"creator\":null, \"creationTime\":null, \"lastUpdateTime\":null}" + ",\"name\":\"WhnManager\",\"group\":\"VREManagement\",\"version\":\"2.0.0-SNAPSHOT\",\"description\":\"Web Hosting Node Service\",\"qualifier\":null,\"optional\":false}" + "}";
String consistsOfString = "{\"" + Element.CLASS_PROPERTY + "\":\"" + ConsistsOf.NAME + "\",\"" + ConsistsOf.PROPAGATION_CONSTRAINT_PROPERTY + "\":"+ pcString + ",\"" + ConsistsOf.HEADER_PROPERTY + "\": " + hString + "," +
"\"" + ConsistsOf.SOURCE_PROPERTY + "\":{\"@class\":\"MyEService\", \"@superClasses\":[\"EService\", \"Service\", \"Resource\"],\"header\":"+ "{\"@class\":\"Header\",\"uuid\":\"4a971915-ca90-48cf-9975-63ee2dd28605\",\"creator\":null, \"creationTime\":null, \"lastUpdateTime\":null}" + "}," +
"\"" + ConsistsOf.TARGET_PROPERTY + "\":{\"@class\":\"MySoftwareFacet\",\"@superClasses\":[\"SoftwareFacet\", \"Facet\"],\"header\":"+ "{\"@class\":\"Header\",\"uuid\":\"3ace4bd0-e5cd-49a3-97a8-a0a9468ce6d4\",\"creator\":null, \"creationTime\":null, \"lastUpdateTime\":null}" + ",\"name\":\"WhnManager\",\"group\":\"VREManagement\",\"version\":\"2.0.0-SNAPSHOT\",\"description\":\"Web Hosting Node Service\",\"qualifier\":null,\"optional\":false}" + "}";
logger.debug(consistsOfString);
@SuppressWarnings("unchecked")