GH-1473: fixing deserialization of index cache issue with duplicated type name attribute

Fixes GH-1473
This commit is contained in:
Martin Lippert
2025-02-07 14:33:21 +01:00
parent 6698801ed4
commit 8f6b5a619a
7 changed files with 73 additions and 20 deletions

View File

@@ -98,5 +98,9 @@ public class Bean extends AbstractSpringIndexElement {
Gson gson = new Gson();
return gson.toJson(this);
}
public Set<String> getSupertypes() {
return supertypes;
}
}

View File

@@ -56,6 +56,8 @@ import com.google.gson.stream.JsonReader;
/**
* @author Martin Lippert
*
* @deprecated Use IndexCacheOnDiscDeltaBased - this class is no longer maintained and up-to-date with the latest index changes
*/
public class IndexCacheOnDisc implements IndexCache {

View File

@@ -367,17 +367,6 @@ public class IndexCacheOnDiscDeltaBased implements IndexCache {
public static Gson createGson() {
return new GsonBuilder()
.registerTypeAdapter(DeltaStorage.class, new DeltaStorageAdapter())
.registerTypeAdapter(Bean.class, new BeanJsonAdapter())
.registerTypeAdapter(InjectionPoint.class, new InjectionPointJsonAdapter())
.registerTypeAdapter(IndexCacheStore.class, new IndexCacheStoreAdapter())
.registerTypeAdapter(SpringIndexElement.class, new SpringIndexElementAdapter())
.create();
}
/**
* just keep a md5 hash internally for identifying files to save memory
*/
@@ -577,6 +566,17 @@ public class IndexCacheOnDiscDeltaBased implements IndexCache {
//
public static Gson createGson() {
return new GsonBuilder()
.registerTypeAdapter(DeltaStorage.class, new DeltaStorageAdapter())
.registerTypeAdapter(Bean.class, new BeanJsonAdapter())
.registerTypeAdapter(InjectionPoint.class, new InjectionPointJsonAdapter())
.registerTypeAdapter(IndexCacheStore.class, new IndexCacheStoreAdapter())
.registerTypeAdapter(SpringIndexElement.class, new SpringIndexElementAdapter())
.create();
}
private static class IndexCacheStoreAdapter implements JsonDeserializer<IndexCacheStore<?>> {
@SuppressWarnings({ "rawtypes", "unchecked" })
@@ -635,7 +635,7 @@ public class IndexCacheOnDiscDeltaBased implements IndexCache {
}
}
private static class BeanJsonAdapter implements JsonDeserializer<Bean> {
private static class BeanJsonAdapter implements JsonSerializer<Bean>, JsonDeserializer<Bean> {
@Override
public Bean deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
@@ -670,6 +670,30 @@ public class IndexCacheOnDiscDeltaBased implements IndexCache {
return bean;
}
@Override
public JsonElement serialize(Bean src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject bean = new JsonObject();
bean.addProperty("name", src.getName());
bean.addProperty("type", src.getType());
bean.add("location", context.serialize(src.getLocation()));
bean.add("injectionPoints", context.serialize(src.getInjectionPoints()));
bean.add("supertypes", context.serialize(src.getSupertypes()));
bean.add("annotations", context.serialize(src.getAnnotations()));
bean.addProperty("isConfiguration", src.isConfiguration());
Type childrenListType = TypeToken.getParameterized(List.class, SpringIndexElement.class).getType();
bean.add("children", context.serialize(src.getChildren(), childrenListType));
bean.addProperty("_internal_node_type", src.getClass().getName());
return bean;
}
}
private static class InjectionPointJsonAdapter implements JsonDeserializer<InjectionPoint> {
@@ -696,14 +720,14 @@ public class IndexCacheOnDiscDeltaBased implements IndexCache {
@Override
public JsonElement serialize(SpringIndexElement element, Type typeOfSrc, JsonSerializationContext context) {
JsonElement elem = context.serialize(element);
elem.getAsJsonObject().addProperty("type", element.getClass().getName());
elem.getAsJsonObject().addProperty("_internal_node_type", element.getClass().getName());
return elem;
}
@Override
public SpringIndexElement deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
String typeName = jsonObject.get("type").getAsString();
String typeName = jsonObject.get("_internal_node_type").getAsString();
try {
return context.deserialize(jsonObject, (Class<?>) Class.forName(typeName));

View File

@@ -57,7 +57,7 @@ public class SpringFactoriesIndexer implements SpringIndexer {
// whenever the implementation of the indexer changes in a way that the stored data in the cache is no longer valid,
// we need to change the generation - this will result in a re-indexing due to no up-to-date cache data being found
private static final String GENERATION = "GEN-10";
private static final String GENERATION = "GEN-11";
private static final String FILE_PATTERN = "**/META-INF/spring/*.factories";

View File

@@ -92,7 +92,7 @@ public class SpringIndexerJava implements SpringIndexer {
// whenever the implementation of the indexer changes in a way that the stored data in the cache is no longer valid,
// we need to change the generation - this will result in a re-indexing due to no up-to-date cache data being found
private static final String GENERATION = "GEN-14";
private static final String GENERATION = "GEN-15";
private static final String INDEX_FILES_TASK_ID = "index-java-source-files-task-";
private static final String SYMBOL_KEY = "symbols";

View File

@@ -54,7 +54,7 @@ public class SpringIndexerXML implements SpringIndexer {
// whenever the implementation of the indexer changes in a way that the stored data in the cache is no longer valid,
// we need to change the generation - this will result in a re-indexing due to no up-to-date cache data being found
private static final String GENERATION = "GEN-9";
private static final String GENERATION = "GEN-10";
private static final String SYMBOL_KEY = "symbols";
private static final String BEANS_KEY = "beans";

View File

@@ -13,7 +13,6 @@ package org.springframework.ide.vscode.boot.index.test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -250,9 +249,9 @@ public class SpringMetamodelIndexTest {
InjectionPoint point2 = new InjectionPoint("point2", "point2-type", locationForDoc1, null);
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, new InjectionPoint[] {point1, point2}, Set.of("supertype1", "supertype2"), emptyAnnotations, true);
String serialized = bean1.toString();
Gson gson = IndexCacheOnDiscDeltaBased.createGson();
String serialized = gson.toJson(bean1);
Bean deserializedBean = gson.fromJson(serialized, Bean.class);
assertEquals("beanName1", deserializedBean.getName());
@@ -301,9 +300,9 @@ public class SpringMetamodelIndexTest {
@Test
void testEmptyInjectionPointsOptimizationWithSerializeDeserializeBeans() {
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations, false);
String serialized = bean1.toString();
Gson gson = IndexCacheOnDiscDeltaBased.createGson();
String serialized = gson.toJson(bean1);
Bean deserializedBean = gson.fromJson(serialized, Bean.class);
assertEquals("beanName1", deserializedBean.getName());
@@ -446,6 +445,30 @@ public class SpringMetamodelIndexTest {
void testSerializeDeserializeBeansWithChildElements() {
Gson gson = IndexCacheOnDiscDeltaBased.createGson();
Bean bean1 = new Bean("beanName1", "beanType1", locationForDoc1, emptyInjectionPoints, Set.of("supertype1", "supertype2"), emptyAnnotations, false);
Bean bean2 = new Bean("beanName2", "beanType2", locationForDoc1, emptyInjectionPoints, Set.of("supertype3", "supertype4, supertype5"), emptyAnnotations, false);
Bean bean3 = new Bean("beanName3", "beanType1", locationForDoc1, emptyInjectionPoints, Set.of("supertype1", "supertype2"), emptyAnnotations, false);
Bean bean4 = new Bean("beanName4", "beanType2", locationForDoc1, emptyInjectionPoints, Set.of("supertype3", "supertype4, supertype5"), emptyAnnotations, false);
bean1.addChild(bean2);
bean2.addChild(bean3);
bean3.addChild(bean4);
String serialized = gson.toJson(bean1);
Bean newBean = gson.fromJson(serialized, Bean.class);
assertEquals("beanName1", newBean.getName());
assertEquals("beanName2", ((Bean) newBean.getChildren().get(0)).getName());
assertEquals("beanName3", ((Bean) newBean.getChildren().get(0).getChildren().get(0)).getName());
assertEquals("beanName4", ((Bean) newBean.getChildren().get(0).getChildren().get(0).getChildren().get(0)).getName());
}
@Test
void testSerializeDeserializeIndexElementsWithChildElements() {
Gson gson = IndexCacheOnDiscDeltaBased.createGson();
SubType2 childOfChild = new SubType2();
SubType1 child1 = new SubType1();