[Major] Refactored locking xmlpers to always first lock parent, and unlock in TX
This commit is contained in:
parent
eefdbb7613
commit
49731f5862
|
@ -115,10 +115,6 @@ public class FileDao {
|
||||||
throw new IllegalArgumentException("IdRefs don't reference directories!"); //$NON-NLS-1$
|
throw new IllegalArgumentException("IdRefs don't reference directories!"); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
|
|
||||||
objectRef.lock();
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
File directoryPath = objectRef.getPath(this.pathBuilder);
|
File directoryPath = objectRef.getPath(this.pathBuilder);
|
||||||
if (!directoryPath.getAbsolutePath().startsWith(this.pathBuilder.getRootPath().getAbsolutePath())) {
|
if (!directoryPath.getAbsolutePath().startsWith(this.pathBuilder.getRootPath().getAbsolutePath())) {
|
||||||
String msg = "The path for {0} is invalid as not child of {1}"; //$NON-NLS-1$
|
String msg = "The path for {0} is invalid as not child of {1}"; //$NON-NLS-1$
|
||||||
|
@ -159,10 +155,6 @@ public class FileDao {
|
||||||
// recursively delete
|
// recursively delete
|
||||||
ObjectRef parent = objectRef.getParent(this.tx);
|
ObjectRef parent = objectRef.getParent(this.tx);
|
||||||
deleteEmptyDirectories(parent);
|
deleteEmptyDirectories(parent);
|
||||||
|
|
||||||
} finally {
|
|
||||||
objectRef.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logPath(IoOperation operation, File path, ObjectRef objectRef) {
|
private void logPath(IoOperation operation, File path, ObjectRef objectRef) {
|
||||||
|
@ -175,17 +167,12 @@ public class FileDao {
|
||||||
|
|
||||||
private void createMissingParents(File path, ObjectRef objectRef) {
|
private void createMissingParents(File path, ObjectRef objectRef) {
|
||||||
ObjectRef parentRef = objectRef.getParent(this.tx);
|
ObjectRef parentRef = objectRef.getParent(this.tx);
|
||||||
parentRef.lock();
|
|
||||||
try {
|
|
||||||
File parentFile = parentRef.getPath(this.pathBuilder);
|
File parentFile = parentRef.getPath(this.pathBuilder);
|
||||||
if (!parentFile.exists() && !parentFile.mkdirs()) {
|
if (!parentFile.exists() && !parentFile.mkdirs()) {
|
||||||
String msg = "Could not create parent path for {0} at {1}"; //$NON-NLS-1$
|
String msg = "Could not create parent path for {0} at {1}"; //$NON-NLS-1$
|
||||||
msg = MessageFormat.format(msg, objectRef.getName(), path.getAbsolutePath());
|
msg = MessageFormat.format(msg, objectRef.getName(), path.getAbsolutePath());
|
||||||
throw new XmlPersistenceException(msg);
|
throw new XmlPersistenceException(msg);
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
parentRef.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertPathIsFileAndWritable(File path, ObjectRef objectRef) {
|
private void assertPathIsFileAndWritable(File path, ObjectRef objectRef) {
|
||||||
|
|
|
@ -26,9 +26,8 @@ import javax.xml.stream.XMLStreamWriter;
|
||||||
import javax.xml.transform.*;
|
import javax.xml.transform.*;
|
||||||
import javax.xml.transform.dom.DOMSource;
|
import javax.xml.transform.dom.DOMSource;
|
||||||
import javax.xml.transform.stream.StreamResult;
|
import javax.xml.transform.stream.StreamResult;
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
import java.io.FileWriter;
|
import java.nio.charset.Charset;
|
||||||
import java.io.IOException;
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
import javanet.staxutils.IndentingXMLStreamWriter;
|
import javanet.staxutils.IndentingXMLStreamWriter;
|
||||||
|
@ -59,27 +58,29 @@ public class FileIo {
|
||||||
|
|
||||||
public <T> void writeSax(PersistenceContext<T> ctx) {
|
public <T> void writeSax(PersistenceContext<T> ctx) {
|
||||||
|
|
||||||
XMLStreamWriter writer;
|
XMLStreamWriter xmlWriter;
|
||||||
try {
|
try {
|
||||||
try (FileWriter fileWriter = new FileWriter(this.tmpPath)) {
|
try (Writer ioWriter = new OutputStreamWriter(new FileOutputStream(this.tmpPath), DEFAULT_ENCODING)) {
|
||||||
|
|
||||||
XMLOutputFactory factory = XMLOutputFactory.newInstance();
|
XMLOutputFactory factory = XMLOutputFactory.newInstance();
|
||||||
writer = factory.createXMLStreamWriter(fileWriter);
|
xmlWriter = factory.createXMLStreamWriter(ioWriter);
|
||||||
writer = new IndentingXMLStreamWriter(writer);
|
xmlWriter = new IndentingXMLStreamWriter(xmlWriter);
|
||||||
|
|
||||||
// start document
|
// start document
|
||||||
writer.writeStartDocument(DEFAULT_ENCODING, DEFAULT_XML_VERSION);
|
xmlWriter.writeStartDocument(DEFAULT_ENCODING, DEFAULT_XML_VERSION);
|
||||||
|
|
||||||
// then delegate object writing to caller
|
// then delegate object writing to caller
|
||||||
SaxParser<T> saxParser = ctx.getParserFactor().getSaxParser();
|
SaxParser<T> saxParser = ctx.getParserFactor().getSaxParser();
|
||||||
saxParser.setObject(ctx.getObject());
|
saxParser.setObject(ctx.getObject());
|
||||||
saxParser.write(writer);
|
saxParser.write(xmlWriter);
|
||||||
|
|
||||||
// and now end
|
// and now end
|
||||||
writer.writeEndDocument();
|
xmlWriter.writeEndDocument();
|
||||||
writer.flush();
|
xmlWriter.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.path.exists() && !this.path.delete())
|
||||||
|
throw new IllegalStateException("Failed to delete existing file " + this.path.getAbsolutePath());
|
||||||
if (!this.tmpPath.renameTo(this.path)) {
|
if (!this.tmpPath.renameTo(this.path)) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Failed to rename temp file " + this.tmpPath.getName() + " to " + this.path.getAbsolutePath());
|
"Failed to rename temp file " + this.tmpPath.getName() + " to " + this.path.getAbsolutePath());
|
||||||
|
@ -158,21 +159,25 @@ public class FileIo {
|
||||||
// transformer.setOutputProperty("{http://xml.apache.org/xalan}line-separator", "\t");
|
// transformer.setOutputProperty("{http://xml.apache.org/xalan}line-separator", "\t");
|
||||||
|
|
||||||
// Transform to file
|
// Transform to file
|
||||||
|
try (Writer ioWriter = new OutputStreamWriter(new FileOutputStream(this.tmpPath), encoding)) {
|
||||||
StreamResult result = new StreamResult(this.tmpPath);
|
StreamResult result = new StreamResult(this.tmpPath);
|
||||||
Source xmlSource = new DOMSource(document);
|
Source xmlSource = new DOMSource(document);
|
||||||
transformer.transform(xmlSource, result);
|
transformer.transform(xmlSource, result);
|
||||||
|
}
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
String msg = MessageFormat.format("Wrote DOM to {0}", this.tmpPath.getAbsolutePath()); //$NON-NLS-1$
|
String msg = MessageFormat.format("Wrote DOM to {0}", this.tmpPath.getAbsolutePath()); //$NON-NLS-1$
|
||||||
logger.info(msg);
|
logger.info(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.path.exists() && !this.path.delete())
|
||||||
|
throw new IllegalStateException("Failed to delete existing file " + this.path.getAbsolutePath());
|
||||||
if (!this.tmpPath.renameTo(this.path)) {
|
if (!this.tmpPath.renameTo(this.path)) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Failed to rename temp file " + this.tmpPath.getName() + " to " + this.path.getAbsolutePath());
|
"Failed to rename temp file " + this.tmpPath.getName() + " to " + this.path.getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (TransformerFactoryConfigurationError | TransformerException e) {
|
} catch (IOException | TransformerFactoryConfigurationError | TransformerException e) {
|
||||||
if (this.tmpPath.exists()) {
|
if (this.tmpPath.exists()) {
|
||||||
if (!this.tmpPath.delete())
|
if (!this.tmpPath.delete())
|
||||||
logger.error("Failed to delete existing temp file " + this.tmpPath.getAbsolutePath());
|
logger.error("Failed to delete existing temp file " + this.tmpPath.getAbsolutePath());
|
||||||
|
|
|
@ -49,8 +49,7 @@ public class MetadataDao {
|
||||||
assertNotClosed(this.tx);
|
assertNotClosed(this.tx);
|
||||||
assertNotIdRef(parentRef);
|
assertNotIdRef(parentRef);
|
||||||
|
|
||||||
parentRef.lock();
|
this.tx.lock(parentRef);
|
||||||
try {
|
|
||||||
File queryPath = parentRef.getPath(this.pathBuilder);
|
File queryPath = parentRef.getPath(this.pathBuilder);
|
||||||
Set<String> keySet = queryTypeSet(queryPath);
|
Set<String> keySet = queryTypeSet(queryPath);
|
||||||
|
|
||||||
|
@ -61,9 +60,6 @@ public class MetadataDao {
|
||||||
}
|
}
|
||||||
|
|
||||||
return keySet;
|
return keySet;
|
||||||
} finally {
|
|
||||||
parentRef.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> queryKeySet(ObjectRef parentRef) {
|
public Set<String> queryKeySet(ObjectRef parentRef) {
|
||||||
|
@ -75,8 +71,7 @@ public class MetadataDao {
|
||||||
assertNotRootRef(parentRef);
|
assertNotRootRef(parentRef);
|
||||||
assertNotIdRef(parentRef);
|
assertNotIdRef(parentRef);
|
||||||
|
|
||||||
parentRef.lock();
|
this.tx.lock(parentRef);
|
||||||
try {
|
|
||||||
File queryPath = parentRef.getPath(this.pathBuilder);
|
File queryPath = parentRef.getPath(this.pathBuilder);
|
||||||
Set<String> keySet = queryKeySet(queryPath, reverse);
|
Set<String> keySet = queryKeySet(queryPath, reverse);
|
||||||
|
|
||||||
|
@ -87,9 +82,6 @@ public class MetadataDao {
|
||||||
}
|
}
|
||||||
|
|
||||||
return keySet;
|
return keySet;
|
||||||
} finally {
|
|
||||||
parentRef.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long queryTypeSize(ObjectRef parentRef) {
|
public long queryTypeSize(ObjectRef parentRef) {
|
||||||
|
@ -97,8 +89,7 @@ public class MetadataDao {
|
||||||
assertNotRootRef(parentRef);
|
assertNotRootRef(parentRef);
|
||||||
assertNotIdRef(parentRef);
|
assertNotIdRef(parentRef);
|
||||||
|
|
||||||
parentRef.lock();
|
this.tx.lock(parentRef);
|
||||||
try {
|
|
||||||
File queryPath = parentRef.getPath(this.pathBuilder);
|
File queryPath = parentRef.getPath(this.pathBuilder);
|
||||||
long numberOfFiles = queryTypeSize(queryPath);
|
long numberOfFiles = queryTypeSize(queryPath);
|
||||||
|
|
||||||
|
@ -109,16 +100,12 @@ public class MetadataDao {
|
||||||
}
|
}
|
||||||
|
|
||||||
return numberOfFiles;
|
return numberOfFiles;
|
||||||
} finally {
|
|
||||||
parentRef.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long querySize(ObjectRef parentRef) {
|
public long querySize(ObjectRef parentRef) {
|
||||||
assertNotClosed(this.tx);
|
assertNotClosed(this.tx);
|
||||||
|
|
||||||
parentRef.lock();
|
this.tx.lock(parentRef);
|
||||||
try {
|
|
||||||
File queryPath = parentRef.getPath(this.pathBuilder);
|
File queryPath = parentRef.getPath(this.pathBuilder);
|
||||||
long numberOfFiles = querySize(queryPath);
|
long numberOfFiles = querySize(queryPath);
|
||||||
|
|
||||||
|
@ -129,9 +116,6 @@ public class MetadataDao {
|
||||||
}
|
}
|
||||||
|
|
||||||
return numberOfFiles;
|
return numberOfFiles;
|
||||||
} finally {
|
|
||||||
parentRef.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -45,77 +45,80 @@ public class ObjectDao {
|
||||||
assertNotClosed();
|
assertNotClosed();
|
||||||
assertNotNull(object);
|
assertNotNull(object);
|
||||||
PersistenceContext<T> ctx = createCtx(object);
|
PersistenceContext<T> ctx = createCtx(object);
|
||||||
ctx.getObjectRef().lock();
|
this.tx.lock(ctx.getObjectRef().getParent(this.tx));
|
||||||
|
this.tx.lock(ctx.getObjectRef());
|
||||||
this.objectFilter.add(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
this.objectFilter.add(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> void addAll(List<T> objects) {
|
public <T> void addAll(List<T> objects) {
|
||||||
assertNotClosed();
|
assertNotClosed();
|
||||||
assertNotNull(objects);
|
assertNotNull(objects);
|
||||||
if (!objects.isEmpty()) {
|
if (objects.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
for (T object : objects) {
|
for (T object : objects) {
|
||||||
PersistenceContext<T> ctx = createCtx(object);
|
PersistenceContext<T> ctx = createCtx(object);
|
||||||
ctx.getObjectRef().lock();
|
this.tx.lock(ctx.getObjectRef().getParent(this.tx));
|
||||||
|
this.tx.lock(ctx.getObjectRef());
|
||||||
this.objectFilter.add(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
this.objectFilter.add(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void update(T object) {
|
public <T> void update(T object) {
|
||||||
assertNotClosed();
|
assertNotClosed();
|
||||||
assertNotNull(object);
|
assertNotNull(object);
|
||||||
PersistenceContext<T> ctx = createCtx(object);
|
PersistenceContext<T> ctx = createCtx(object);
|
||||||
ctx.getObjectRef().lock();
|
this.tx.lock(ctx.getObjectRef());
|
||||||
this.objectFilter.update(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
this.objectFilter.update(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> void updateAll(List<T> objects) {
|
public <T> void updateAll(List<T> objects) {
|
||||||
assertNotClosed();
|
assertNotClosed();
|
||||||
assertNotNull(objects);
|
assertNotNull(objects);
|
||||||
if (!objects.isEmpty()) {
|
if (objects.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
for (T object : objects) {
|
for (T object : objects) {
|
||||||
PersistenceContext<T> ctx = createCtx(object);
|
PersistenceContext<T> ctx = createCtx(object);
|
||||||
ctx.getObjectRef().lock();
|
this.tx.lock(ctx.getObjectRef());
|
||||||
this.objectFilter.update(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
this.objectFilter.update(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void remove(T object) {
|
public <T> void remove(T object) {
|
||||||
assertNotClosed();
|
assertNotClosed();
|
||||||
assertNotNull(object);
|
assertNotNull(object);
|
||||||
PersistenceContext<T> ctx = createCtx(object);
|
PersistenceContext<T> ctx = createCtx(object);
|
||||||
ctx.getObjectRef().lock();
|
this.tx.lock(ctx.getObjectRef().getParent(this.tx));
|
||||||
|
this.tx.lock(ctx.getObjectRef());
|
||||||
this.objectFilter.remove(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
this.objectFilter.remove(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> void removeAll(List<T> objects) {
|
public <T> void removeAll(List<T> objects) {
|
||||||
assertNotClosed();
|
assertNotClosed();
|
||||||
assertNotNull(objects);
|
assertNotNull(objects);
|
||||||
if (!objects.isEmpty()) {
|
if (objects.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
for (T object : objects) {
|
for (T object : objects) {
|
||||||
PersistenceContext<T> ctx = createCtx(object);
|
PersistenceContext<T> ctx = createCtx(object);
|
||||||
ctx.getObjectRef().lock();
|
this.tx.lock(ctx.getObjectRef().getParent(this.tx));
|
||||||
|
this.tx.lock(ctx.getObjectRef());
|
||||||
this.objectFilter.remove(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
this.objectFilter.remove(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public <T> long removeAllBy(TypeRef typeRef) {
|
public <T> long removeAllBy(TypeRef typeRef) {
|
||||||
assertNotClosed();
|
assertNotClosed();
|
||||||
|
|
||||||
long removed = 0;
|
long removed = 0;
|
||||||
|
|
||||||
Set<ObjectRef> refs = new TreeSet<>();
|
this.tx.lock(typeRef);
|
||||||
typeRef.lock();
|
|
||||||
refs.add(typeRef);
|
|
||||||
try {
|
|
||||||
|
|
||||||
Set<String> types = this.tx.getMetadataDao().queryTypeSet(typeRef);
|
Set<String> types = this.tx.getMetadataDao().queryTypeSet(typeRef);
|
||||||
for (String type : types) {
|
for (String type : types) {
|
||||||
ObjectRef childTypeRef = typeRef.getChildTypeRef(this.tx, type);
|
ObjectRef childTypeRef = typeRef.getChildTypeRef(this.tx, type);
|
||||||
childTypeRef.lock();
|
this.tx.lock(childTypeRef);
|
||||||
refs.add(childTypeRef);
|
|
||||||
|
|
||||||
Set<String> ids = queryKeySet(childTypeRef, false);
|
Set<String> ids = queryKeySet(childTypeRef, false);
|
||||||
for (String id : ids) {
|
for (String id : ids) {
|
||||||
|
@ -123,16 +126,11 @@ public class ObjectDao {
|
||||||
ObjectRef idRef = childTypeRef.getChildIdRef(this.tx, id);
|
ObjectRef idRef = childTypeRef.getChildIdRef(this.tx, id);
|
||||||
|
|
||||||
PersistenceContext<T> ctx = createCtx(idRef);
|
PersistenceContext<T> ctx = createCtx(idRef);
|
||||||
ctx.getObjectRef().lock();
|
this.tx.lock(ctx.getObjectRef());
|
||||||
this.objectFilter.remove(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
this.objectFilter.remove(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
||||||
removed++;
|
removed++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
for (ObjectRef ref : refs) {
|
|
||||||
ref.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
@ -144,21 +142,18 @@ public class ObjectDao {
|
||||||
|
|
||||||
long removed = 0;
|
long removed = 0;
|
||||||
|
|
||||||
subTypeRef.lock();
|
this.tx.lock(subTypeRef);
|
||||||
try {
|
|
||||||
Set<String> ids = queryKeySet(subTypeRef, false);
|
Set<String> ids = queryKeySet(subTypeRef, false);
|
||||||
for (String id : ids) {
|
for (String id : ids) {
|
||||||
|
|
||||||
ObjectRef idRef = subTypeRef.getChildIdRef(this.tx, id);
|
ObjectRef idRef = subTypeRef.getChildIdRef(this.tx, id);
|
||||||
|
|
||||||
PersistenceContext<T> ctx = createCtx(idRef);
|
PersistenceContext<T> ctx = createCtx(idRef);
|
||||||
ctx.getObjectRef().lock();
|
this.tx.lock(ctx.getObjectRef());
|
||||||
this.objectFilter.remove(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
this.objectFilter.remove(ctx.getObjectRef().getType(), ctx.getObjectRef(), ctx);
|
||||||
removed++;
|
removed++;
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
subTypeRef.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
@ -167,7 +162,8 @@ public class ObjectDao {
|
||||||
assertNotClosed();
|
assertNotClosed();
|
||||||
assertIsIdRef(objectRef);
|
assertIsIdRef(objectRef);
|
||||||
PersistenceContext<T> ctx = createCtx(objectRef);
|
PersistenceContext<T> ctx = createCtx(objectRef);
|
||||||
ctx.getObjectRef().lock();
|
this.tx.lock(ctx.getObjectRef().getParent(this.tx));
|
||||||
|
this.tx.lock(ctx.getObjectRef());
|
||||||
this.objectFilter.remove(objectRef.getType(), ctx.getObjectRef(), ctx);
|
this.objectFilter.remove(objectRef.getType(), ctx.getObjectRef(), ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,47 +172,35 @@ public class ObjectDao {
|
||||||
assertIsNotIdRef(parentRef);
|
assertIsNotIdRef(parentRef);
|
||||||
assertIsNotRootRef(parentRef);
|
assertIsNotRootRef(parentRef);
|
||||||
|
|
||||||
parentRef.lock();
|
this.tx.lock(parentRef);
|
||||||
try {
|
|
||||||
|
|
||||||
Set<String> keySet = queryKeySet(parentRef, false);
|
Set<String> keySet = queryKeySet(parentRef, false);
|
||||||
for (String id : keySet) {
|
for (String id : keySet) {
|
||||||
|
|
||||||
ObjectRef childRef = parentRef.getChildIdRef(this.tx, id);
|
ObjectRef childRef = parentRef.getChildIdRef(this.tx, id);
|
||||||
PersistenceContext<T> ctx = createCtx(childRef);
|
PersistenceContext<T> ctx = createCtx(childRef);
|
||||||
ctx.getObjectRef().lock();
|
this.tx.lock(ctx.getObjectRef());
|
||||||
this.objectFilter.remove(childRef.getType(), ctx.getObjectRef(), ctx);
|
this.objectFilter.remove(childRef.getType(), ctx.getObjectRef(), ctx);
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
parentRef.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> boolean hasElement(ObjectRef objectRef) {
|
public <T> boolean hasElement(ObjectRef objectRef) {
|
||||||
assertNotClosed();
|
assertNotClosed();
|
||||||
assertIsIdRef(objectRef);
|
assertIsIdRef(objectRef);
|
||||||
|
|
||||||
objectRef.lock();
|
this.tx.lock(objectRef);
|
||||||
try {
|
|
||||||
PersistenceContext<T> ctx = objectRef.<T>createPersistenceContext(this.tx);
|
PersistenceContext<T> ctx = objectRef.<T>createPersistenceContext(this.tx);
|
||||||
return this.fileDao.exists(ctx);
|
return this.fileDao.exists(ctx);
|
||||||
} finally {
|
|
||||||
objectRef.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T queryById(ObjectRef objectRef) {
|
public <T> T queryById(ObjectRef objectRef) {
|
||||||
assertNotClosed();
|
assertNotClosed();
|
||||||
assertIsIdRef(objectRef);
|
assertIsIdRef(objectRef);
|
||||||
|
|
||||||
objectRef.lock();
|
this.tx.lock(objectRef);
|
||||||
try {
|
|
||||||
PersistenceContext<T> ctx = objectRef.<T>createPersistenceContext(this.tx);
|
PersistenceContext<T> ctx = objectRef.<T>createPersistenceContext(this.tx);
|
||||||
this.fileDao.performRead(ctx);
|
this.fileDao.performRead(ctx);
|
||||||
return ctx.getObject();
|
return ctx.getObject();
|
||||||
} finally {
|
|
||||||
objectRef.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> List<T> queryAll(ObjectRef parentRef) {
|
public <T> List<T> queryAll(ObjectRef parentRef) {
|
||||||
|
@ -227,8 +211,7 @@ public class ObjectDao {
|
||||||
assertNotClosed();
|
assertNotClosed();
|
||||||
assertIsNotIdRef(parentRef);
|
assertIsNotIdRef(parentRef);
|
||||||
|
|
||||||
parentRef.lock();
|
this.tx.lock(parentRef);
|
||||||
try {
|
|
||||||
|
|
||||||
MetadataDao metadataDao = this.tx.getMetadataDao();
|
MetadataDao metadataDao = this.tx.getMetadataDao();
|
||||||
Set<String> keySet = metadataDao.queryKeySet(parentRef, reverse);
|
Set<String> keySet = metadataDao.queryKeySet(parentRef, reverse);
|
||||||
|
@ -239,50 +222,34 @@ public class ObjectDao {
|
||||||
|
|
||||||
ObjectRef childRef = parentRef.getChildIdRef(this.tx, id);
|
ObjectRef childRef = parentRef.getChildIdRef(this.tx, id);
|
||||||
PersistenceContext<T> childCtx = childRef.createPersistenceContext(this.tx);
|
PersistenceContext<T> childCtx = childRef.createPersistenceContext(this.tx);
|
||||||
childCtx.getObjectRef().lock();
|
this.tx.lock(childCtx.getObjectRef());
|
||||||
try {
|
|
||||||
this.fileDao.performRead(childCtx);
|
this.fileDao.performRead(childCtx);
|
||||||
assertObjectRead(childCtx);
|
assertObjectRead(childCtx);
|
||||||
result.add(childCtx.getObject());
|
result.add(childCtx.getObject());
|
||||||
} finally {
|
|
||||||
childCtx.getObjectRef().unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxSize != Integer.MAX_VALUE && i >= maxSize)
|
if (maxSize != Integer.MAX_VALUE && i >= maxSize)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
} finally {
|
|
||||||
parentRef.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> queryKeySet(ObjectRef parentRef, boolean reverse) {
|
private Set<String> queryKeySet(ObjectRef parentRef, boolean reverse) {
|
||||||
assertNotClosed();
|
assertNotClosed();
|
||||||
assertIsNotIdRef(parentRef);
|
assertIsNotIdRef(parentRef);
|
||||||
|
|
||||||
parentRef.lock();
|
this.tx.lock(parentRef);
|
||||||
try {
|
|
||||||
MetadataDao metadataDao = this.tx.getMetadataDao();
|
MetadataDao metadataDao = this.tx.getMetadataDao();
|
||||||
return metadataDao.queryKeySet(parentRef, reverse);
|
return metadataDao.queryKeySet(parentRef, reverse);
|
||||||
} finally {
|
|
||||||
parentRef.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long querySize(ObjectRef parentRef) {
|
public long querySize(ObjectRef parentRef) {
|
||||||
assertNotClosed();
|
assertNotClosed();
|
||||||
assertIsNotIdRef(parentRef);
|
assertIsNotIdRef(parentRef);
|
||||||
|
|
||||||
parentRef.lock();
|
this.tx.lock(parentRef);
|
||||||
try {
|
|
||||||
MetadataDao metadataDao = this.tx.getMetadataDao();
|
MetadataDao metadataDao = this.tx.getMetadataDao();
|
||||||
return metadataDao.querySize(parentRef);
|
return metadataDao.querySize(parentRef);
|
||||||
} finally {
|
|
||||||
parentRef.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> PersistenceContext<T> createCtx(T object) {
|
public <T> PersistenceContext<T> createCtx(T object) {
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package li.strolch.xmlpers.api;
|
package li.strolch.xmlpers.api;
|
||||||
|
|
||||||
|
import li.strolch.xmlpers.objref.LockableObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||||
*/
|
*/
|
||||||
|
@ -61,4 +63,6 @@ public interface PersistenceTransaction extends AutoCloseable {
|
||||||
public FileDao getFileDao();
|
public FileDao getFileDao();
|
||||||
|
|
||||||
public PersistenceManager getManager();
|
public PersistenceManager getManager();
|
||||||
|
|
||||||
|
void lock(LockableObject lockableObject);
|
||||||
}
|
}
|
|
@ -15,6 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package li.strolch.xmlpers.impl;
|
package li.strolch.xmlpers.impl;
|
||||||
|
|
||||||
|
import static li.strolch.utils.helper.PropertiesHelper.*;
|
||||||
|
import static li.strolch.xmlpers.api.PersistenceConstants.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
@ -49,25 +52,14 @@ public class DefaultPersistenceManager implements PersistenceManager {
|
||||||
String context = DefaultPersistenceManager.class.getSimpleName();
|
String context = DefaultPersistenceManager.class.getSimpleName();
|
||||||
|
|
||||||
// get properties
|
// get properties
|
||||||
boolean verbose = PropertiesHelper
|
boolean verbose = getPropertyBool(properties, context, PROP_VERBOSE, Boolean.FALSE);
|
||||||
.getPropertyBool(properties, context, PersistenceConstants.PROP_VERBOSE, Boolean.FALSE);
|
IoMode ioMode = IoMode.valueOf(getProperty(properties, context, PROP_XML_IO_MOD, IoMode.DOM.name()));
|
||||||
String ioModeS = PropertiesHelper
|
long lockTime = getPropertyLong(properties, context, PROP_LOCK_TIME_MILLIS, LockableObject.getLockTime());
|
||||||
.getProperty(properties, context, PersistenceConstants.PROP_XML_IO_MOD, IoMode.DOM.name());
|
String basePath = getProperty(properties, context, PROP_BASEPATH, null);
|
||||||
IoMode ioMode = IoMode.valueOf(ioModeS);
|
|
||||||
long lockTime = PropertiesHelper
|
|
||||||
.getPropertyLong(properties, context, PersistenceConstants.PROP_LOCK_TIME_MILLIS, 10000L);
|
|
||||||
String basePath = PropertiesHelper.getProperty(properties, context, PersistenceConstants.PROP_BASEPATH, null);
|
|
||||||
|
|
||||||
// set lock time on LockableObject
|
// set lock time on LockableObject
|
||||||
try {
|
if (lockTime != LockableObject.getLockTime())
|
||||||
Field lockTimeField = LockableObject.class.getDeclaredField("tryLockTime");//$NON-NLS-1$
|
LockableObject.setTryLockTime(lockTime);
|
||||||
lockTimeField.setAccessible(true);
|
|
||||||
lockTimeField.setLong(null, lockTime);
|
|
||||||
logger.info("Using a max lock acquire time of " + StringHelper
|
|
||||||
.formatMillisecondsDuration(lockTime)); //$NON-NLS-1$
|
|
||||||
} catch (SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
|
|
||||||
throw new RuntimeException("Failed to configure tryLockTime on LockableObject!", e); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate base path exists and is writable
|
// validate base path exists and is writable
|
||||||
File basePathF = new File(basePath).getAbsoluteFile();
|
File basePathF = new File(basePath).getAbsoluteFile();
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.util.*;
|
||||||
|
|
||||||
import li.strolch.utils.objectfilter.ObjectFilter;
|
import li.strolch.utils.objectfilter.ObjectFilter;
|
||||||
import li.strolch.xmlpers.api.*;
|
import li.strolch.xmlpers.api.*;
|
||||||
|
import li.strolch.xmlpers.objref.LockableObject;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -48,6 +49,8 @@ public class DefaultPersistenceTransaction implements PersistenceTransaction {
|
||||||
private Date startTimeDate;
|
private Date startTimeDate;
|
||||||
private TransactionResult txResult;
|
private TransactionResult txResult;
|
||||||
|
|
||||||
|
private Set<LockableObject> lockedObjects;
|
||||||
|
|
||||||
public DefaultPersistenceTransaction(PersistenceManager manager, IoMode ioMode, boolean verbose) {
|
public DefaultPersistenceTransaction(PersistenceManager manager, IoMode ioMode, boolean verbose) {
|
||||||
this.startTime = System.nanoTime();
|
this.startTime = System.nanoTime();
|
||||||
this.startTimeDate = new Date();
|
this.startTimeDate = new Date();
|
||||||
|
@ -60,6 +63,7 @@ public class DefaultPersistenceTransaction implements PersistenceTransaction {
|
||||||
|
|
||||||
this.closeStrategy = TransactionCloseStrategy.COMMIT;
|
this.closeStrategy = TransactionCloseStrategy.COMMIT;
|
||||||
this.state = TransactionState.OPEN;
|
this.state = TransactionState.OPEN;
|
||||||
|
this.lockedObjects = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -113,14 +117,13 @@ public class DefaultPersistenceTransaction implements PersistenceTransaction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void autoCloseableRollback() {
|
public void autoCloseableRollback() {
|
||||||
|
try {
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
if (this.state == TransactionState.COMMITTED)
|
if (this.state == TransactionState.COMMITTED)
|
||||||
throw new IllegalStateException("Transaction has already been committed!"); //$NON-NLS-1$
|
throw new IllegalStateException("Transaction has already been committed!"); //$NON-NLS-1$
|
||||||
|
|
||||||
if (this.state != TransactionState.ROLLED_BACK) {
|
if (this.state != TransactionState.ROLLED_BACK) {
|
||||||
unlockObjectRefs();
|
|
||||||
this.state = TransactionState.ROLLED_BACK;
|
this.state = TransactionState.ROLLED_BACK;
|
||||||
this.objectFilter.clearCache();
|
|
||||||
|
|
||||||
long end = System.nanoTime();
|
long end = System.nanoTime();
|
||||||
long txDuration = end - this.startTime;
|
long txDuration = end - this.startTime;
|
||||||
|
@ -135,6 +138,11 @@ public class DefaultPersistenceTransaction implements PersistenceTransaction {
|
||||||
this.txResult.setModificationByKey(Collections.emptyMap());
|
this.txResult.setModificationByKey(Collections.emptyMap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
// clean up
|
||||||
|
this.objectFilter.clearCache();
|
||||||
|
releaseAllLocks();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void internalCommit() {
|
private void internalCommit() {
|
||||||
|
@ -242,10 +250,9 @@ public class DefaultPersistenceTransaction implements PersistenceTransaction {
|
||||||
this.txResult.setFailCause(e);
|
this.txResult.setFailCause(e);
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
unlockObjectRefs();
|
|
||||||
this.objectFilter.clearCache();
|
this.objectFilter.clearCache();
|
||||||
|
releaseAllLocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
long end = System.nanoTime();
|
long end = System.nanoTime();
|
||||||
|
@ -274,16 +281,21 @@ public class DefaultPersistenceTransaction implements PersistenceTransaction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
private void releaseAllLocks() {
|
||||||
private void unlockObjectRefs() {
|
for (LockableObject lockedObject : this.lockedObjects) {
|
||||||
List<PersistenceContext> lockedObjects = this.objectFilter.getAll(PersistenceContext.class);
|
lockedObject.releaseLock();
|
||||||
for (PersistenceContext lockedObject : lockedObjects) {
|
|
||||||
lockedObject.getObjectRef().unlock();
|
|
||||||
}
|
}
|
||||||
|
this.lockedObjects.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOpen() {
|
public boolean isOpen() {
|
||||||
return this.state == TransactionState.OPEN;
|
return this.state == TransactionState.OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lock(LockableObject lockableObject) {
|
||||||
|
lockableObject.lock();
|
||||||
|
this.lockedObjects.add(lockableObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package li.strolch.xmlpers.objref;
|
package li.strolch.xmlpers.objref;
|
||||||
|
|
||||||
|
import static java.lang.Thread.currentThread;
|
||||||
|
import static java.text.MessageFormat.format;
|
||||||
|
import static li.strolch.utils.helper.StringHelper.formatMillisecondsDuration;
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
@ -29,12 +34,22 @@ public class LockableObject {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(LockableObject.class);
|
private static final Logger logger = LoggerFactory.getLogger(LockableObject.class);
|
||||||
private static long tryLockTime = 10000L;
|
private static long tryLockTime = 10000L;
|
||||||
|
|
||||||
private final ReentrantLock lock;
|
public static void setTryLockTime(long tryLockTime) {
|
||||||
|
LockableObject.tryLockTime = tryLockTime;
|
||||||
|
}
|
||||||
|
|
||||||
public LockableObject() {
|
private final ReentrantLock lock;
|
||||||
|
protected final String name;
|
||||||
|
|
||||||
|
public LockableObject(String name) {
|
||||||
|
this.name = name;
|
||||||
this.lock = new ReentrantLock(true);
|
this.lock = new ReentrantLock(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
public static long getLockTime() {
|
public static long getLockTime() {
|
||||||
return tryLockTime;
|
return tryLockTime;
|
||||||
}
|
}
|
||||||
|
@ -43,11 +58,32 @@ public class LockableObject {
|
||||||
* @see java.util.concurrent.locks.ReentrantLock#tryLock(long, TimeUnit)
|
* @see java.util.concurrent.locks.ReentrantLock#tryLock(long, TimeUnit)
|
||||||
*/
|
*/
|
||||||
public void lock() {
|
public void lock() {
|
||||||
|
|
||||||
|
// don't lock multiple times
|
||||||
|
if (this.lock.isHeldByCurrentThread() && this.lock.isLocked())
|
||||||
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (!this.lock.tryLock(tryLockTime, TimeUnit.MILLISECONDS)) {
|
if (!this.lock.tryLock(tryLockTime, TimeUnit.MILLISECONDS)) {
|
||||||
String msg = "Failed to acquire lock after {0} for {1}"; //$NON-NLS-1$
|
String msg = "Thread {0} failed to acquire lock after {1} for {2}"; //$NON-NLS-1$
|
||||||
msg = MessageFormat.format(msg, StringHelper.formatMillisecondsDuration(tryLockTime), toString());
|
msg = format(msg, currentThread().getName(), formatMillisecondsDuration(tryLockTime), this);
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.error(msg);
|
||||||
|
logger.error("Listing all active threads: ");
|
||||||
|
Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
|
||||||
|
for (Thread thread : allStackTraces.keySet()) {
|
||||||
|
StackTraceElement[] trace = allStackTraces.get(thread);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (StackTraceElement traceElement : trace)
|
||||||
|
sb.append("\n\tat ").append(traceElement);
|
||||||
|
logger.error("\nThread " + thread.getName() + ":\n" + sb.toString() + "\n");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to log active threads: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
throw new XmlPersistenceException(msg);
|
throw new XmlPersistenceException(msg);
|
||||||
}
|
}
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
|
@ -60,9 +96,11 @@ public class LockableObject {
|
||||||
/**
|
/**
|
||||||
* @see java.util.concurrent.locks.ReentrantLock#unlock()
|
* @see java.util.concurrent.locks.ReentrantLock#unlock()
|
||||||
*/
|
*/
|
||||||
public void unlock() {
|
public void releaseLock() {
|
||||||
this.lock.unlock();
|
while (this.lock.isHeldByCurrentThread() && this.lock.isLocked()) {
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
logger.debug("unlocking " + toString()); //$NON-NLS-1$
|
logger.debug("unlocking " + toString()); //$NON-NLS-1$
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,14 +24,8 @@ import li.strolch.xmlpers.impl.PathBuilder;
|
||||||
|
|
||||||
public abstract class ObjectRef extends LockableObject implements Comparable<ObjectRef> {
|
public abstract class ObjectRef extends LockableObject implements Comparable<ObjectRef> {
|
||||||
|
|
||||||
protected final String name;
|
|
||||||
|
|
||||||
protected ObjectRef(String name) {
|
protected ObjectRef(String name) {
|
||||||
this.name = name;
|
super(name);
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getPath(PathBuilder pathBuilder) {
|
public File getPath(PathBuilder pathBuilder) {
|
||||||
|
|
|
@ -15,10 +15,14 @@
|
||||||
*/
|
*/
|
||||||
package li.strolch.xmlpers.test;
|
package li.strolch.xmlpers.test;
|
||||||
|
|
||||||
|
import static li.strolch.utils.helper.SystemHelper.isLinux;
|
||||||
|
import static li.strolch.utils.helper.SystemHelper.isWindows;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import li.strolch.utils.helper.FileHelper;
|
import li.strolch.utils.helper.FileHelper;
|
||||||
|
import li.strolch.utils.helper.SystemHelper;
|
||||||
import li.strolch.xmlpers.api.IoMode;
|
import li.strolch.xmlpers.api.IoMode;
|
||||||
import li.strolch.xmlpers.api.PersistenceConstants;
|
import li.strolch.xmlpers.api.PersistenceConstants;
|
||||||
import li.strolch.xmlpers.api.PersistenceManager;
|
import li.strolch.xmlpers.api.PersistenceManager;
|
||||||
|
@ -42,8 +46,14 @@ public abstract class AbstractPersistenceTest {
|
||||||
|
|
||||||
File file = new File(path).getAbsoluteFile();
|
File file = new File(path).getAbsoluteFile();
|
||||||
File parent = file.getParentFile();
|
File parent = file.getParentFile();
|
||||||
|
if (isWindows()) {
|
||||||
|
if (!parent.getAbsolutePath().endsWith("\\target\\db"))
|
||||||
|
throw new RuntimeException("Bad parent! Must be \\target\\db: " + parent);
|
||||||
|
} else {
|
||||||
if (!parent.getAbsolutePath().endsWith("/target/db"))
|
if (!parent.getAbsolutePath().endsWith("/target/db"))
|
||||||
throw new RuntimeException("Bad parent! Must be /target/db/: " + parent);
|
throw new RuntimeException("Bad parent! Must be /target/db: " + parent);
|
||||||
|
}
|
||||||
|
|
||||||
if (!parent.exists() && !parent.mkdirs())
|
if (!parent.exists() && !parent.mkdirs())
|
||||||
throw new RuntimeException("Failed to create path " + parent);
|
throw new RuntimeException("Failed to create path " + parent);
|
||||||
|
|
||||||
|
@ -51,7 +61,7 @@ public abstract class AbstractPersistenceTest {
|
||||||
if (!FileHelper.deleteFiles(file.listFiles(), true))
|
if (!FileHelper.deleteFiles(file.listFiles(), true))
|
||||||
throw new RuntimeException("Could not clean up path " + file.getAbsolutePath());
|
throw new RuntimeException("Could not clean up path " + file.getAbsolutePath());
|
||||||
|
|
||||||
if (!file.exists() && !file.mkdir())
|
if (!file.exists() && !file.mkdirs())
|
||||||
throw new RuntimeException("Failed to create path " + file);
|
throw new RuntimeException("Failed to create path " + file);
|
||||||
|
|
||||||
File domFile = new File(file, IoMode.DOM.name());
|
File domFile = new File(file, IoMode.DOM.name());
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class LockingTest extends AbstractPersistenceTest {
|
||||||
properties.setProperty(PersistenceConstants.PROP_LOCK_TIME_MILLIS, Long.toString(500L));
|
properties.setProperty(PersistenceConstants.PROP_LOCK_TIME_MILLIS, Long.toString(500L));
|
||||||
setup(properties);
|
setup(properties);
|
||||||
|
|
||||||
this.waitForWorkersTime = LockableObject.getLockTime() + getWaitForWorkersTime() + 300L;
|
this.waitForWorkersTime = LockableObject.getLockTime() + 300L;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -112,15 +112,18 @@ public class LockingTest extends AbstractPersistenceTest {
|
||||||
logger.info("Setup thread " + worker.getName()); //$NON-NLS-1$
|
logger.info("Setup thread " + worker.getName()); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
|
|
||||||
// create resource which is to be updated
|
int nrOfSuccess;
|
||||||
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
|
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
|
||||||
|
|
||||||
|
// create resource which is to be updated
|
||||||
MyModel resource = createResource(resourceId);
|
MyModel resource = createResource(resourceId);
|
||||||
tx.getObjectDao().add(resource);
|
tx.getObjectDao().add(resource);
|
||||||
|
|
||||||
|
// and before closing the TX, run the workers, which should fail as we are still holding the locks
|
||||||
|
nrOfSuccess = runWorkers(workers);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nrOfSuccess = runWorkers(workers);
|
assertEquals("Only one thread should be able to perform the TX!", 0, nrOfSuccess); //$NON-NLS-1$
|
||||||
|
|
||||||
assertEquals("Only one thread should be able to perform the TX!", 1, nrOfSuccess); //$NON-NLS-1$
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int runWorkers(List<? extends AbstractWorker> workers) throws InterruptedException {
|
private int runWorkers(List<? extends AbstractWorker> workers) throws InterruptedException {
|
||||||
|
@ -128,7 +131,7 @@ public class LockingTest extends AbstractPersistenceTest {
|
||||||
setRun(true);
|
setRun(true);
|
||||||
|
|
||||||
for (AbstractWorker worker : workers) {
|
for (AbstractWorker worker : workers) {
|
||||||
worker.join(getWaitForWorkersTime() + 2000L);
|
worker.join(getWaitForWorkersTime() + 5000L);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nrOfSuccess = 0;
|
int nrOfSuccess = 0;
|
||||||
|
@ -177,16 +180,9 @@ public class LockingTest extends AbstractPersistenceTest {
|
||||||
logger.info("Starting work..."); //$NON-NLS-1$
|
logger.info("Starting work..."); //$NON-NLS-1$
|
||||||
try (PersistenceTransaction tx = LockingTest.this.persistenceManager.openTx()) {
|
try (PersistenceTransaction tx = LockingTest.this.persistenceManager.openTx()) {
|
||||||
doWork(tx);
|
doWork(tx);
|
||||||
|
|
||||||
try {
|
|
||||||
Thread.sleep(getWaitForWorkersTime());
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.success = true;
|
this.success = true;
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("Work completed."); //$NON-NLS-1$
|
logger.info("Work completed."); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue