Jackson serialization for project managers

This commit is contained in:
Antonin Delpeuch 2018-09-30 11:28:41 +01:00
parent 49b84574da
commit 987c2d1c80
4 changed files with 118 additions and 36 deletions

View File

@ -54,6 +54,9 @@ import org.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.refine.history.HistoryEntryManager; import com.google.refine.history.HistoryEntryManager;
import com.google.refine.model.Project; import com.google.refine.model.Project;
import com.google.refine.model.metadata.IMetadata; import com.google.refine.model.metadata.IMetadata;
@ -342,6 +345,7 @@ public abstract class ProjectManager {
/** /**
* Gets the InterProjectModel from memory * Gets the InterProjectModel from memory
*/ */
@JsonIgnore
public InterProjectModel getInterProjectModel() { public InterProjectModel getInterProjectModel() {
return _interProjectModel; return _interProjectModel;
} }
@ -472,7 +476,7 @@ public abstract class ProjectManager {
* Gets all the project Metadata currently held in memory. * Gets all the project Metadata currently held in memory.
* @return * @return
*/ */
@JsonIgnore
public Map<Long, ProjectMetadata> getAllProjectMetadata() { public Map<Long, ProjectMetadata> getAllProjectMetadata() {
for(Project project : _projects.values()) { for(Project project : _projects.values()) {
mergeEmptyUserMetadata(project.getMetadata()); mergeEmptyUserMetadata(project.getMetadata());
@ -486,6 +490,7 @@ public abstract class ProjectManager {
* *
* @return * @return
*/ */
@JsonIgnore
public Map<String, Integer> getAllProjectTags() { public Map<String, Integer> getAllProjectTags() {
return _projectsTags; return _projectsTags;
} }
@ -517,6 +522,7 @@ public abstract class ProjectManager {
* Gets the preference store * Gets the preference store
* @return * @return
*/ */
@JsonProperty("preferences")
public PreferenceStore getPreferenceStore() { public PreferenceStore getPreferenceStore() {
return _preferenceStore; return _preferenceStore;
} }
@ -525,6 +531,7 @@ public abstract class ProjectManager {
* Gets all expressions from the preference store * Gets all expressions from the preference store
* @return * @return
*/ */
@JsonIgnore
public List<String> getExpressions() { public List<String> getExpressions() {
return ((TopList) _preferenceStore.get("scripting.expressions")).getList(); return ((TopList) _preferenceStore.get("scripting.expressions")).getList();
} }
@ -533,6 +540,7 @@ public abstract class ProjectManager {
* The history entry manager deals with changes * The history entry manager deals with changes
* @return manager for handling history * @return manager for handling history
*/ */
@JsonIgnore
public abstract HistoryEntryManager getHistoryEntryManager(); public abstract HistoryEntryManager getHistoryEntryManager();

View File

@ -42,7 +42,9 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Properties; import java.util.Properties;
import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
@ -57,6 +59,10 @@ import org.json.JSONWriter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.refine.Jsonizable;
import com.google.refine.ProjectManager; import com.google.refine.ProjectManager;
import com.google.refine.history.HistoryEntryManager; import com.google.refine.history.HistoryEntryManager;
import com.google.refine.model.Project; import com.google.refine.model.Project;
@ -67,7 +73,7 @@ import com.google.refine.model.metadata.ProjectMetadata;
import com.google.refine.preference.TopList; import com.google.refine.preference.TopList;
public class FileProjectManager extends ProjectManager { public class FileProjectManager extends ProjectManager implements Jsonizable {
final static protected String PROJECT_DIR_SUFFIX = ".project"; final static protected String PROJECT_DIR_SUFFIX = ".project";
protected File _workspaceDir; protected File _workspaceDir;
@ -95,6 +101,7 @@ public class FileProjectManager extends ProjectManager {
load(); load();
} }
@JsonIgnore
public File getWorkspaceDir() { public File getWorkspaceDir() {
return _workspaceDir; return _workspaceDir;
} }
@ -107,6 +114,7 @@ public class FileProjectManager extends ProjectManager {
return dir; return dir;
} }
@JsonIgnore
public File getProjectDir(long projectID) { public File getProjectDir(long projectID) {
return getProjectDir(_workspaceDir, projectID); return getProjectDir(_workspaceDir, projectID);
} }
@ -267,6 +275,7 @@ public class FileProjectManager extends ProjectManager {
@Override @Override
protected void saveWorkspace() { protected void saveWorkspace() {
synchronized (this) { synchronized (this) {
// TODO refactor this so that we check if the save is needed before writing to the file!
File tempFile = new File(_workspaceDir, "workspace.temp.json"); File tempFile = new File(_workspaceDir, "workspace.temp.json");
try { try {
if (!saveToFile(tempFile)) { if (!saveToFile(tempFile)) {
@ -294,39 +303,32 @@ public class FileProjectManager extends ProjectManager {
} }
tempFile.renameTo(file); tempFile.renameTo(file);
logger.info("Saved workspace"); logger.info("Saved workspace");
} }
} }
protected boolean saveNeeded() {
boolean projectSaveNeeded = _projectsMetadata.entrySet().stream()
.anyMatch(e -> e.getValue() != null && e.getValue().isDirty());
return projectSaveNeeded || _preferenceStore.isDirty();
}
protected void saveProjectMetadata() throws JSONException, IOException {
for(Entry<Long,ProjectMetadata> entry : _projectsMetadata.entrySet()) {
ProjectMetadata metadata = entry.getValue();
if (metadata != null && metadata.isDirty()) {
ProjectMetadataUtilities.save(metadata, getProjectDir(entry.getKey()));
}
}
}
protected boolean saveToFile(File file) throws IOException, JSONException { protected boolean saveToFile(File file) throws IOException, JSONException {
FileWriter writer = new FileWriter(file); FileWriter writer = new FileWriter(file);
boolean saveWasNeeded = false; boolean saveWasNeeded = saveNeeded();
try { try {
JSONWriter jsonWriter = new JSONWriter(writer); JSONWriter jsonWriter = new JSONWriter(writer);
jsonWriter.object(); write(jsonWriter, new Properties());
jsonWriter.key("projectIDs"); saveProjectMetadata();
jsonWriter.array();
for (Long id : _projectsMetadata.keySet()) {
ProjectMetadata metadata = _projectsMetadata.get(id);
if (metadata != null) {
jsonWriter.value(id);
if (metadata.isDirty()) {
Project project = ProjectManager.singleton.getProject(id);
metadata.setRowCount(project.rows.size());
ProjectMetadataUtilities.save(metadata, getProjectDir(id));
saveWasNeeded = true;
}
}
}
jsonWriter.endArray();
writer.write('\n');
jsonWriter.key("preferences");
saveWasNeeded |= _preferenceStore.isDirty();
_preferenceStore.write(jsonWriter, new Properties());
jsonWriter.endObject();
} finally { } finally {
writer.close(); writer.close();
} }
@ -484,4 +486,28 @@ public class FileProjectManager extends ProjectManager {
gos.close(); gos.close();
} }
} }
@Override
public void write(JSONWriter jsonWriter, Properties options)
throws JSONException {
jsonWriter.object();
jsonWriter.key("projectIDs");
jsonWriter.array();
for (Long id : _projectsMetadata.keySet()) {
ProjectMetadata metadata = _projectsMetadata.get(id);
if (metadata != null) {
jsonWriter.value(id);
}
}
jsonWriter.endArray();
jsonWriter.key("preferences");
_preferenceStore.write(jsonWriter, new Properties());
jsonWriter.endObject();
}
@JsonProperty("projectIDs")
public Set<Long> getProjectIds() {
return _projectsMetadata.keySet();
}
} }

View File

@ -0,0 +1,45 @@
package com.google.refine.tests.io;
import java.io.File;
import org.testng.annotations.Test;
import static org.mockito.Mockito.mock;
import com.google.refine.io.FileProjectManager;
import com.google.refine.model.metadata.ProjectMetadata;
import com.google.refine.tests.RefineTest;
import com.google.refine.tests.util.TestUtils;
public class FileProjectManagerTests extends RefineTest {
protected class FileProjectManagerStub extends FileProjectManager {
protected FileProjectManagerStub(File dir) {
super(dir);
_projectsMetadata.put(1234L, mock(ProjectMetadata.class));
}
}
@Test
public void serializeFileProjectManager() {
FileProjectManager manager = new FileProjectManagerStub(workspaceDir);
String json = "{\n" +
" \"preferences\" : {\n" +
" \"entries\" : {\n" +
" \"scripting.expressions\" : {\n" +
" \"class\" : \"com.google.refine.preference.TopList\",\n" +
" \"list\" : [ ],\n" +
" \"top\" : 100\n" +
" },\n" +
" \"scripting.starred-expressions\" : {\n" +
" \"class\" : \"com.google.refine.preference.TopList\",\n" +
" \"list\" : [ ],\n" +
" \"top\" : 2147483647\n" +
" }\n" +
" }\n" +
" },\n" +
" \"projectIDs\" : [ 1234 ]\n" +
" }";
TestUtils.isSerializedTo(manager, json);
}
}

View File

@ -7,12 +7,11 @@ import org.json.JSONObject;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.refine.preference.PreferenceStore; import com.google.refine.preference.PreferenceStore;
import com.google.refine.tests.RefineTest;
import com.google.refine.tests.util.TestUtils; import com.google.refine.tests.util.TestUtils;
public class PreferenceStoreTests { public class PreferenceStoreTests {
@Test public static String json = "{"
public void serializePreferenceStore() {
String json = "{"
+ "\"entries\":{" + "\"entries\":{"
+ " \"reconciliation.standardServices\":[" + " \"reconciliation.standardServices\":["
+ " {\"propose_properties\":{\"service_url\":\"https://tools.wmflabs.org/openrefine-wikidata\",\"service_path\":\"/en/propose_properties\"},\"preview\":{\"width\":320,\"url\":\"https://tools.wmflabs.org/openrefine-wikidata/en/preview?id={{id}}\",\"height\":90},\"view\":{\"url\":\"https://www.wikidata.org/wiki/{{id}}\"},\"ui\":{\"handler\":\"ReconStandardServicePanel\"},\"identifierSpace\":\"http://www.wikidata.org/entity/\",\"name\":\"Wikidata Reconciliation for OpenRefine (en)\",\"suggest\":{\"property\":{\"flyout_service_path\":\"/en/flyout/property?id=${id}\",\"service_url\":\"https://tools.wmflabs.org/openrefine-wikidata\",\"service_path\":\"/en/suggest/property\"},\"type\":{\"flyout_service_path\":\"/en/flyout/type?id=${id}\",\"service_url\":\"https://tools.wmflabs.org/openrefine-wikidata\",\"service_path\":\"/en/suggest/type\"},\"entity\":{\"flyout_service_path\":\"/en/flyout/entity?id=${id}\",\"service_url\":\"https://tools.wmflabs.org/openrefine-wikidata\",\"service_path\":\"/en/suggest/entity\"}},\"defaultTypes\":[{\"name\":\"entity\",\"id\":\"Q35120\"}],\"url\":\"https://tools.wmflabs.org/openrefine-wikidata/en/api\",\"schemaSpace\":\"http://www.wikidata.org/prop/direct/\"}" + " {\"propose_properties\":{\"service_url\":\"https://tools.wmflabs.org/openrefine-wikidata\",\"service_path\":\"/en/propose_properties\"},\"preview\":{\"width\":320,\"url\":\"https://tools.wmflabs.org/openrefine-wikidata/en/preview?id={{id}}\",\"height\":90},\"view\":{\"url\":\"https://www.wikidata.org/wiki/{{id}}\"},\"ui\":{\"handler\":\"ReconStandardServicePanel\"},\"identifierSpace\":\"http://www.wikidata.org/entity/\",\"name\":\"Wikidata Reconciliation for OpenRefine (en)\",\"suggest\":{\"property\":{\"flyout_service_path\":\"/en/flyout/property?id=${id}\",\"service_url\":\"https://tools.wmflabs.org/openrefine-wikidata\",\"service_path\":\"/en/suggest/property\"},\"type\":{\"flyout_service_path\":\"/en/flyout/type?id=${id}\",\"service_url\":\"https://tools.wmflabs.org/openrefine-wikidata\",\"service_path\":\"/en/suggest/type\"},\"entity\":{\"flyout_service_path\":\"/en/flyout/entity?id=${id}\",\"service_url\":\"https://tools.wmflabs.org/openrefine-wikidata\",\"service_path\":\"/en/suggest/entity\"}},\"defaultTypes\":[{\"name\":\"entity\",\"id\":\"Q35120\"}],\"url\":\"https://tools.wmflabs.org/openrefine-wikidata/en/api\",\"schemaSpace\":\"http://www.wikidata.org/prop/direct/\"}"
@ -20,6 +19,10 @@ public class PreferenceStoreTests {
+ " \"scripting.starred-expressions\":{\"class\":\"com.google.refine.preference.TopList\",\"top\":2147483647,\"list\":[]}," + " \"scripting.starred-expressions\":{\"class\":\"com.google.refine.preference.TopList\",\"top\":2147483647,\"list\":[]},"
+ " \"scripting.expressions\":{\"class\":\"com.google.refine.preference.TopList\",\"top\":100,\"list\":[]}" + " \"scripting.expressions\":{\"class\":\"com.google.refine.preference.TopList\",\"top\":100,\"list\":[]}"
+ "}}"; + "}}";
@Test
public void serializePreferenceStore() {
String jsonAfter = "{" String jsonAfter = "{"
+ "\"entries\":{" + "\"entries\":{"
+ " \"reconciliation.standardServices\":[" + " \"reconciliation.standardServices\":["