Type deserialization is compliant with old version without CHANGELOG

This commit is contained in:
Luca Frosini 2020-12-14 10:10:49 +01:00
parent 745bb9eb3e
commit 21fcb502f2
3 changed files with 90 additions and 69 deletions

View File

@ -34,6 +34,7 @@ public class TypeMapper {
static { static {
mapper = new ObjectMapper(); mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, false);
mapper.registerSubtypes(Type.class); mapper.registerSubtypes(Type.class);

View File

@ -32,151 +32,159 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class TypeImpl implements Type { public class TypeImpl implements Type {
private static Logger logger = LoggerFactory.getLogger(TypeImpl.class); private static Logger logger = LoggerFactory.getLogger(TypeImpl.class);
/** /**
* Generated Serial version UID * Generated Serial version UID
*/ */
private static final long serialVersionUID = -4333954207969059451L; private static final long serialVersionUID = -4333954207969059451L;
public final static String DESCRIPTION = "DESCRIPTION"; public final static String DESCRIPTION = "DESCRIPTION";
public final static String VERSION = "VERSION"; public final static String VERSION = "VERSION";
public final static String CHANGELOG = "CHANGELOG"; public final static String CHANGELOG = "CHANGELOG";
public static final Map<TypeVersion, String> DEFAULT_CHANGELOG_MAP; public static final Map<TypeVersion, String> DEFAULT_CHANGELOG_MAP;
private static final Map<String, String> DEFAULT_CHANGELOG_MAP_KEY_AS_STRING;
static { static {
DEFAULT_CHANGELOG_MAP = new HashMap<>(); DEFAULT_CHANGELOG_MAP = new HashMap<>();
DEFAULT_CHANGELOG_MAP.put(new TypeVersion(TypeVersion.MINIMAL_VERSION_STRING), "First Version"); DEFAULT_CHANGELOG_MAP.put(new TypeVersion(TypeVersion.MINIMAL_VERSION_STRING), TypeVersion.MINIMAL_VERSION_DESCRIPTION);
DEFAULT_CHANGELOG_MAP_KEY_AS_STRING = new HashMap<>();
DEFAULT_CHANGELOG_MAP_KEY_AS_STRING.put(TypeVersion.MINIMAL_VERSION_STRING, TypeVersion.MINIMAL_VERSION_DESCRIPTION);
} }
protected Header header; protected Header header;
protected String name; protected String name;
protected String description; protected String description;
protected TypeVersion version; protected TypeVersion version;
@JsonProperty(value = CHANGELOG_PROPERTY, required = false)
@JsonInclude(Include.NON_NULL)
protected Map<TypeVersion, String> changelog; protected Map<TypeVersion, String> changelog;
@JsonProperty(value="abstract") @JsonProperty(value = "abstract")
protected boolean abstractType; protected boolean abstractType;
protected Set<String> superClasses; protected Set<String> superClasses;
protected Set<PropertyDefinition> properties; protected Set<PropertyDefinition> properties;
protected <E extends Element> Set<String> retrieveSuperClasses(Class<? extends E> type, Class<E> baseClass, String topSuperClass){ protected <E extends Element> Set<String> retrieveSuperClasses(Class<? extends E> type, Class<E> baseClass,
String topSuperClass) {
Set<String> interfaceList = new HashSet<>(); Set<String> interfaceList = new HashSet<>();
if(type==baseClass){ if (type == baseClass) {
if(topSuperClass!=null) { if (topSuperClass != null) {
interfaceList.add(topSuperClass); interfaceList.add(topSuperClass);
} }
return interfaceList; return interfaceList;
} }
Class<?>[] interfaces = type.getInterfaces(); Class<?>[] interfaces = type.getInterfaces();
for (Class<?> interfaceClass : interfaces) { for (Class<?> interfaceClass : interfaces) {
if(!baseClass.isAssignableFrom(interfaceClass)){ if (!baseClass.isAssignableFrom(interfaceClass)) {
continue; continue;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Class<? extends Element> clz = (Class<? extends Element>) interfaceClass; Class<? extends Element> clz = (Class<? extends Element>) interfaceClass;
interfaceList.add(TypeMapper.getType(clz)); interfaceList.add(TypeMapper.getType(clz));
} }
return interfaceList; return interfaceList;
} }
protected Set<PropertyDefinition> retrieveListOfProperties(Class<?> type){ protected Set<PropertyDefinition> retrieveListOfProperties(Class<?> type) {
Set<PropertyDefinition> properties = new HashSet<>(); Set<PropertyDefinition> properties = new HashSet<>();
for (Method m : type.getDeclaredMethods()){ for (Method m : type.getDeclaredMethods()) {
m.setAccessible(true); m.setAccessible(true);
if(m.isAnnotationPresent(ISProperty.class)){ if (m.isAnnotationPresent(ISProperty.class)) {
if(m.isBridge()) { if (m.isBridge()) {
continue; continue;
} }
ISProperty propAnnotation = m.getAnnotation(ISProperty.class); ISProperty propAnnotation = m.getAnnotation(ISProperty.class);
PropertyDefinition prop = new PropertyDefinitionImpl(propAnnotation, m); PropertyDefinition prop = new PropertyDefinitionImpl(propAnnotation, m);
properties.add(prop); properties.add(prop);
logger.trace("Property {} retrieved in type {} ", prop, type.getSimpleName()); logger.trace("Property {} retrieved in type {} ", prop, type.getSimpleName());
} }
} }
return properties; return properties;
} }
protected Class<?> getGenericClass(java.lang.reflect.Type type){ protected Class<?> getGenericClass(java.lang.reflect.Type type) {
TypeVariable<?> typeVariable = (TypeVariable<?>) type; TypeVariable<?> typeVariable = (TypeVariable<?>) type;
java.lang.reflect.Type[] bounds = typeVariable.getBounds(); java.lang.reflect.Type[] bounds = typeVariable.getBounds();
java.lang.reflect.Type t = bounds[0]; java.lang.reflect.Type t = bounds[0];
if(t instanceof ParameterizedType) { if (t instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) t; ParameterizedType parameterizedType = (ParameterizedType) t;
return (Class<?>) parameterizedType.getRawType(); return (Class<?>) parameterizedType.getRawType();
} }
return (Class<?>) t; return (Class<?>) t;
} }
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({ "rawtypes", "unchecked" })
public static Type getInstance(Class<? extends Element> clz) { public static Type getInstance(Class<? extends Element> clz) {
Type typeDefinition = null; Type typeDefinition = null;
try { try {
if(EntityElement.class.isAssignableFrom(clz)) { if (EntityElement.class.isAssignableFrom(clz)) {
typeDefinition = EntityTypeImpl.getEntityTypeDefinitionInstance((Class<? extends EntityElement>) clz); typeDefinition = EntityTypeImpl.getEntityTypeDefinitionInstance((Class<? extends EntityElement>) clz);
return typeDefinition; return typeDefinition;
} else if(RelationElement.class.isAssignableFrom(clz)){ } else if (RelationElement.class.isAssignableFrom(clz)) {
typeDefinition = RelationTypeImpl.getRelationTypeDefinitionInstance((Class<? extends RelationElement<?,?>>) clz); typeDefinition = RelationTypeImpl
.getRelationTypeDefinitionInstance((Class<? extends RelationElement<?, ?>>) clz);
return typeDefinition; return typeDefinition;
} else if(PropertyElement.class.isAssignableFrom(clz)){ } else if (PropertyElement.class.isAssignableFrom(clz)) {
typeDefinition = new PropertyTypeImpl(clz); typeDefinition = new PropertyTypeImpl(clz);
return typeDefinition; return typeDefinition;
} else if(Type.class.isAssignableFrom(clz)) { } else if (Type.class.isAssignableFrom(clz)) {
typeDefinition = new TypeImpl(clz); typeDefinition = new TypeImpl(clz);
return typeDefinition; return typeDefinition;
} else { } else {
throw new RuntimeException("Serialization required"); throw new RuntimeException("Serialization required");
} }
} finally { } finally {
if(typeDefinition!=null) { if (typeDefinition != null) {
logger.debug("{} : {} ", clz, typeDefinition); logger.debug("{} : {} ", clz, typeDefinition);
} }
} }
} }
protected TypeImpl() {} protected TypeImpl() {
}
protected TypeImpl(Class<? extends Element> clz) { protected TypeImpl(Class<? extends Element> clz) {
this.name = TypeMapper.getType(clz); this.name = TypeMapper.getType(clz);
this.description = TypeMapper.getStaticStringFieldByName(clz, DESCRIPTION, ""); this.description = TypeMapper.getStaticStringFieldByName(clz, DESCRIPTION, "");
String versionString = TypeMapper.getStaticStringFieldByName(clz, VERSION, TypeVersion.MINIMAL_VERSION_STRING); String versionString = TypeMapper.getStaticStringFieldByName(clz, VERSION, TypeVersion.MINIMAL_VERSION_STRING);
this.version = new TypeVersion(versionString); this.version = new TypeVersion(versionString);
this.changelog = TypeMapper.getStaticFieldByName(clz, CHANGELOG, DEFAULT_CHANGELOG_MAP); this.changelog = TypeMapper.getStaticFieldByName(clz, CHANGELOG, DEFAULT_CHANGELOG_MAP);
if(!changelog.containsKey(version)) { if (!changelog.containsKey(version)) {
throw new RuntimeException("The Type " + name + " does not provided the appropriated changelog Map"); throw new RuntimeException("The Type " + name + " does not provided the appropriated changelog Map");
} }
this.abstractType = false; this.abstractType = false;
if(clz.isAnnotationPresent(Abstract.class)){ if (clz.isAnnotationPresent(Abstract.class)) {
this.abstractType = true; this.abstractType = true;
} }
} }
@Override @Override
public Header getHeader() { public Header getHeader() {
return header; return header;
} }
@Override @Override
public void setHeader(Header header){ public void setHeader(Header header) {
this.header = header; this.header = header;
} }
@Override @Override
public String getName() { public String getName() {
return name; return name;
@ -186,17 +194,17 @@ public class TypeImpl implements Type {
public String getDescription() { public String getDescription() {
return description; return description;
} }
@Override @Override
public TypeVersion getVersion() { public TypeVersion getVersion() {
return version; return version;
} }
@JsonGetter(value=VERSION_PROPERTY) @JsonGetter(value = VERSION_PROPERTY)
protected String getVersionAsString() { protected String getVersionAsString() {
return version.toString(); return version.toString();
} }
@JsonSetter(value = VERSION_PROPERTY) @JsonSetter(value = VERSION_PROPERTY)
protected void setVersion(String version) { protected void setVersion(String version) {
this.version = new TypeVersion(version); this.version = new TypeVersion(version);
@ -206,24 +214,32 @@ public class TypeImpl implements Type {
public Map<TypeVersion, String> getChangelog() { public Map<TypeVersion, String> getChangelog() {
return changelog; return changelog;
} }
@JsonGetter(value=CHANGELOG_PROPERTY) @JsonGetter(value = CHANGELOG_PROPERTY)
@JsonInclude(Include.NON_NULL)
public Map<String, String> getChangelogwithVersionAsString() { public Map<String, String> getChangelogwithVersionAsString() {
Map<String,String> map = new HashMap<>(); if(this.changelog==null) {
for(TypeVersion typeVersion : changelog.keySet()) { return DEFAULT_CHANGELOG_MAP_KEY_AS_STRING;
}
Map<String, String> map = new HashMap<>();
for (TypeVersion typeVersion : changelog.keySet()) {
map.put(typeVersion.toString(), changelog.get(typeVersion)); map.put(typeVersion.toString(), changelog.get(typeVersion));
} }
return map; return map;
} }
@JsonSetter(value = CHANGELOG_PROPERTY) @JsonSetter(value=CHANGELOG_PROPERTY)
public void setChangelog(Map<String, String> changelog) { public void setChangelog(Map<String, String> changelog) {
if(changelog==null) {
this.changelog = DEFAULT_CHANGELOG_MAP;
return;
}
this.changelog = new HashMap<>(); this.changelog = new HashMap<>();
for(String version : changelog.keySet()) { for (String version : changelog.keySet()) {
this.changelog.put(new TypeVersion(version), changelog.get(version)); this.changelog.put(new TypeVersion(version), changelog.get(version));
} }
} }
@Override @Override
public boolean isAbstract() { public boolean isAbstract() {
return abstractType; return abstractType;
@ -233,12 +249,10 @@ public class TypeImpl implements Type {
public Set<String> getSuperClasses() { public Set<String> getSuperClasses() {
return superClasses; return superClasses;
} }
@JsonInclude(Include.NON_EMPTY) @JsonInclude(Include.NON_EMPTY)
public Set<PropertyDefinition> getProperties() { public Set<PropertyDefinition> getProperties() {
return properties; return properties;
} }
} }

View File

@ -161,6 +161,12 @@ public class SerializationTest {
TypeVersion typeVersion = propertyType.getVersion(); TypeVersion typeVersion = propertyType.getVersion();
logger.debug("Version {}", typeVersion.toString()); logger.debug("Version {}", typeVersion.toString());
logger.info(ElementMapper.marshal(propertyType)); logger.info(ElementMapper.marshal(propertyType));
String json = "{\"@class\":\"PropertyType\",\"header\":null,\"name\":\"Header\",\"description\":\"\",\"superClasses\":[\"Property\"],\"properties\":[{\"@class\":\"PropertyDefinition\",\"name\":\"lastUpdateTime\",\"description\":\"\",\"mandatory\":true,\"readonly\":false,\"notnull\":true,\"max\":null,\"min\":null,\"regexp\":null,\"linkedType\":null,\"linkedClass\":null,\"type\":6},{\"@class\":\"PropertyDefinition\",\"name\":\"modifiedBy\",\"description\":\"\",\"mandatory\":true,\"readonly\":false,\"notnull\":true,\"max\":null,\"min\":null,\"regexp\":null,\"linkedType\":null,\"linkedClass\":null,\"type\":7},{\"@class\":\"PropertyDefinition\",\"name\":\"creator\",\"description\":\"\",\"mandatory\":true,\"readonly\":true,\"notnull\":true,\"max\":null,\"min\":null,\"regexp\":null,\"linkedType\":null,\"linkedClass\":null,\"type\":7},{\"@class\":\"PropertyDefinition\",\"name\":\"creationTime\",\"description\":\"\",\"mandatory\":true,\"readonly\":true,\"notnull\":true,\"max\":null,\"min\":null,\"regexp\":null,\"linkedType\":null,\"linkedClass\":null,\"type\":6},{\"@class\":\"PropertyDefinition\",\"name\":\"uuid\",\"description\":\"\",\"mandatory\":true,\"readonly\":true,\"notnull\":true,\"max\":null,\"min\":null,\"regexp\":\"^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}){1}$\",\"linkedType\":null,\"linkedClass\":null,\"type\":7}],\"abstract\":false,\"version\":\"1.0.0\"}";
logger.info(json);
@SuppressWarnings("unchecked")
PropertyType<Header> headerType = (PropertyType<Header>) TypeMapper.deserializeTypeDefinition(json);
logger.info(ElementMapper.marshal(headerType));
} }
} }