Commit b3a8ef05 authored by mhellka's avatar mhellka
Browse files

Clear attributes from metadata table for removed files.

parent 4931ad0b
Pipeline #118715 passed with stages
in 17 minutes and 21 seconds
......@@ -348,10 +348,13 @@ class ArchiveImpl implements CDStarArchive {
}
synchronized AttributeCache getMeta() {
if (metaCache == null) {
checkPermission(ArchivePermission.READ_META);
checkPermission(ArchivePermission.READ_META);
return getMetaNoCheck();
}
private synchronized AttributeCache getMetaNoCheck() {
if (metaCache == null)
metaCache = new AttributeCache(this);
}
return metaCache;
}
......@@ -424,7 +427,11 @@ class ArchiveImpl implements CDStarArchive {
requirePayloadWriteable();
checkPermission(ArchivePermission.CHANGE_FILES);
// Must be loaded before file is removed, or it will complain about references
// to missing files.
getMetaNoCheck();
if (filesCache.remove(file)) {
getMetaNoCheck().onFileRemoved(file);
file.resource.remove();
markContentModified();
}
......
......@@ -25,17 +25,17 @@ import de.gwdg.cdstar.runtime.client.auth.ArchivePermission;
class AttributeCache {
private final ArchiveImpl a;
Map<String, Map<String, AttributeImpl>> cache;
boolean modified = false;
private final Map<String, Map<String, AttributeImpl>> attrMap;
private boolean modified = false;
private boolean modifiedInternal;
AttributeJsonFormat<AttributeImpl> jsonFormat = new AttributeJsonFormat<AttributeImpl>() {
@Override
public AttributeImpl makeAttr(String fileId, String attrName, List<String> values) {
FileImpl file = null;
if (Utils.notNullOrEmpty(fileId)) {
if (Utils.notNullOrEmpty(fileId))
file = a.getInternalFileById(fileId).orElseThrow(() -> new BackendError.DamagedDataError(
"Unable to load metadata table. Reference to unknown file id: " + fileId));
}
"Metadata table references unknown file id: " + fileId));
return new AttributeImpl(AttributeCache.this, file, attrName, values);
}
......@@ -49,34 +49,27 @@ class AttributeCache {
public AttributeCache(ArchiveImpl a) {
this.a = a;
}
Map<String, Map<String, AttributeImpl>> parse() {
if (cache != null)
return cache;
final Resource src = a.getResource(ArchiveImpl.METAFILE);
if (src == null || src.getSize() == 0) {
return cache = new HashMap<>();
}
try (InputStream is = new BufferedInputStream(Channels.newInputStream(src.getReadChannel(0)))) {
final JsonParser parser = SharedObjectMapper.json.getFactory().createParser(is);
cache = jsonFormat.parse(parser);
parser.close();
return cache;
} catch (final IOException e1) {
throw new BackendError("Failed to load metadata", e1);
attrMap = new HashMap<>();
} else {
try (InputStream is = new BufferedInputStream(Channels.newInputStream(src.getReadChannel(0)))) {
final JsonParser parser = SharedObjectMapper.json.getFactory().createParser(is);
attrMap = jsonFormat.parse(parser);
parser.close();
} catch (final IOException e1) {
throw new BackendError("Failed to load metadata table", e1);
}
}
}
synchronized void flush() {
if (cache == null || !modified)
if (!modified && !modifiedInternal)
return; // Nothing to do
Resource r = a.getResource(ArchiveImpl.METAFILE);
if (r != null && cache.isEmpty()) {
if (r != null && attrMap.isEmpty()) {
r.remove();
return;
}
......@@ -88,7 +81,7 @@ class AttributeCache {
try (BufferedOutputStream out = new BufferedOutputStream(Channels.newOutputStream(r.getWriteChannel(0)))) {
final JsonGenerator gen = SharedObjectMapper.json.getFactory().createGenerator(out);
jsonFormat.serialize(gen, cache);
jsonFormat.serialize(gen, attrMap);
gen.close();
} catch (final IOException e1) {
throw new BackendError("Failed to store metadata", e1);
......@@ -101,18 +94,18 @@ class AttributeCache {
throw new IllegalArgumentException("Not a valid attribute name: " + name);
final String key = file == null ? "" : file.getID();
return parse().computeIfAbsent(key, f -> new HashMap<>()).computeIfAbsent(name,
return attrMap.computeIfAbsent(key, f -> new HashMap<>()).computeIfAbsent(name,
propName -> new AttributeImpl(this, file, name, new ArrayList<>()));
}
public synchronized Set<CDStarAttribute> getAttributes(FileImpl file) {
final String key = file == null ? "" : file.getID();
return new HashSet<>(parse().computeIfAbsent(key, f -> new HashMap<>()).values());
return new HashSet<>(attrMap.computeIfAbsent(key, f -> new HashMap<>()).values());
}
public synchronized Set<String> getAttributeNames(FileImpl file) {
final String key = file == null ? "" : file.getID();
return new HashSet<>(parse().computeIfAbsent(key, f -> new HashMap<>()).keySet());
return new HashSet<>(attrMap.computeIfAbsent(key, f -> new HashMap<>()).keySet());
}
void setModifed(AttributeImpl attr) {
......@@ -126,4 +119,14 @@ class AttributeCache {
a.forEachListener(l -> l.propertyChanged(attr, attr.getFile(), attr.getOrigValues()));
}
/**
* Clears the attributes for a given file, if present. Does not check for
* permissions, as we assume that this is just a side-effect of removing a file.
*/
void onFileRemoved(FileImpl file) {
if (attrMap.remove(file.getID()) != null) {
modifiedInternal = true;
}
}
}
package de.gwdg.cdstar.runtime;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import de.gwdg.cdstar.runtime.client.CDStarArchive;
public class FileTest extends RuntimeBaseTest {
@Test
public void removeFileWithMetadata() throws Exception {
CDStarArchive ar = makeArchive();
ar.createFile("test").getAttribute("dc:title").append("foo");
commit();
ar = loadArchive(ar.getId());
ar.getFile("test").remove();
commit();
ar = loadArchive(ar.getId());
// triggers attribute loading, which should not fail with a damaged data error.
ar.getAttribute("what");
}
@Test
public void removeThenCreateFile() throws Exception {
CDStarArchive ar = makeArchive();
ar.createFile("test").getAttribute("dc:title").append("foo");
commit();
ar = loadArchive(ar.getId());
ar.getFile("test").remove();
ar.createFile("test").getAttribute("dc:title").append("bar");
commit();
ar = loadArchive(ar.getId());
assertEquals("bar", ar.getFile("test").getAttribute("dc:title").getFirst());
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment