use of org.exist.storage.txn.Txn in project exist by eXist-db.
the class BlobStoreImpl method add.
@Override
public Tuple2<BlobId, Long> add(final Txn transaction, final InputStream is) throws IOException {
if (state.get() != State.OPEN) {
throw new IOException("Blob Store is not open!");
}
// stage the BLOB file
final Tuple3<Path, Long, MessageDigest> staged = stage(is);
final BlobVacuum.RequestDeleteStagedBlobFile requestDeleteStagedBlobFile = new BlobVacuum.RequestDeleteStagedBlobFile(stagingDir, staged._1.getFileName().toString());
// register a callback to cleanup the staged BLOB file ONLY after commit+checkpoint
final JournalManager journalManager = database.getJournalManager().orElse(null);
if (journalManager != null) {
final DeleteStagedBlobFile cleanupStagedBlob = new DeleteStagedBlobFile(vacuumQueue, requestDeleteStagedBlobFile);
journalManager.listen(cleanupStagedBlob);
transaction.registerListener(cleanupStagedBlob);
}
final BlobId blobId = new BlobId(staged._3.getValue());
// if the blob entry does not exist, we exclusively compute it as STAGED.
BlobReference blobReference = references.computeIfAbsent(blobId, k -> new BlobReference(STAGED));
try {
while (true) {
if (blobReference.count.compareAndSet(STAGED, PROMOTING)) {
// write journal entries to the WAL
if (journalManager != null) {
try {
journalManager.journal(new StoreBlobFileLoggable(transaction.getId(), blobId, staged._1.getFileName().toString()));
journalManager.journal(new UpdateBlobRefCountLoggable(transaction.getId(), blobId, 0, 1));
// force WAL entries to disk!
journalManager.flush(true, true);
} catch (final JournalException e) {
references.remove(blobId);
throw new IOException(e);
}
}
// promote the staged blob
promote(staged);
if (journalManager == null) {
// no journal (or recovery)... so go ahead and schedule cleanup of the staged blob file
enqueueVacuum(vacuumQueue, requestDeleteStagedBlobFile);
}
// schedule disk persist of the new value
persistQueue.put(Tuple(blobId, blobReference, 1));
// update memory with the new value
blobReference.count.set(1);
// done!
return Tuple(blobId, staged._2);
}
final int count = blobReference.count.get();
// guard against a concurrent #add or #remove
if (count == PROMOTING || count == UPDATING_COUNT) {
// spin whilst another thread promotes the blob, or updates the reference count
// sleep a small time to save CPU
Thread.sleep(10);
continue;
}
// i.e. wait for the deletion of the blob to complete, and then we can add the blob again
if (count == DELETING) {
blobReference = references.computeIfAbsent(blobId, k -> new BlobReference(STAGED));
// loop again
continue;
}
// only increment the blob reference if the blob is active!
if (count >= 0 && blobReference.count.compareAndSet(count, UPDATING_COUNT)) {
// NOTE: we are the only thread that can be in this branch for the blobId
final int newCount = count + 1;
// write journal entries to the WAL
if (journalManager != null) {
try {
journalManager.journal(new UpdateBlobRefCountLoggable(transaction.getId(), blobId, count, newCount));
// force WAL entries to disk!
journalManager.flush(true, true);
} catch (final JournalException e) {
// restore the state of the blobReference first!
blobReference.count.set(count);
throw new IOException(e);
}
}
// persist the new value
persistQueue.put(Tuple(blobId, blobReference, newCount));
// update memory with the new value, and release other spinning threads
blobReference.count.set(newCount);
// done!
return Tuple(blobId, staged._2);
}
}
} catch (final InterruptedException e) {
// thrown by persistQueue.put or Thread.sleep
Thread.currentThread().interrupt();
throw new IOException(e);
}
}
use of org.exist.storage.txn.Txn in project exist by eXist-db.
the class BinaryDoc method eval.
@Override
public Sequence eval(final Sequence[] args, final Sequence contextSequence) throws XPathException {
final Sequence emptyParamReturnValue = (isCalledAs(FS_BINARY_DOC_NAME) || isCalledAs(FS_BINARY_DOC_CONTENT_DIGEST_NAME)) ? Sequence.EMPTY_SEQUENCE : BooleanValue.FALSE;
if (args[0].isEmpty()) {
return emptyParamReturnValue;
}
final String path = args[0].getStringValue();
try (final LockedDocument lockedDoc = context.getBroker().getXMLResource(XmldbURI.xmldbUriFor(path), LockMode.READ_LOCK)) {
if (lockedDoc == null) {
return emptyParamReturnValue;
}
final DocumentImpl doc = lockedDoc.getDocument();
if (doc.getResourceType() != DocumentImpl.BINARY_FILE) {
return emptyParamReturnValue;
} else if (isCalledAs(FS_BINARY_DOC_NAME)) {
try (final Txn transaction = context.getBroker().continueOrBeginTransaction()) {
final BinaryDocument bin = (BinaryDocument) doc;
final InputStream is = context.getBroker().getBinaryResource(transaction, bin);
final Base64BinaryDocument b64doc = Base64BinaryDocument.getInstance(context, is);
b64doc.setUrl(path);
transaction.commit();
return b64doc;
}
} else if (isCalledAs(FS_BINARY_DOC_CONTENT_DIGEST_NAME)) {
final String algorithm = args[1].getStringValue();
final DigestType digestType;
try {
digestType = DigestType.forCommonName(algorithm);
} catch (final IllegalArgumentException e) {
throw new XPathException(this, "Invalid algorithm: " + algorithm, e);
}
try (final Txn transaction = context.getBroker().getBrokerPool().getTransactionManager().beginTransaction()) {
final BinaryDocument bin = (BinaryDocument) doc;
final MessageDigest messageDigest = context.getBroker().getBinaryResourceContentDigest(transaction, bin, digestType);
final InputStream is = new UnsynchronizedByteArrayInputStream(messageDigest.getValue());
final Sequence result = BinaryValueFromInputStream.getInstance(context, new HexBinaryValueType(), is);
transaction.commit();
return result;
}
} else {
return BooleanValue.TRUE;
}
} catch (final URISyntaxException e) {
logger.error("Invalid resource URI", e);
throw new XPathException(this, "Invalid resource uri", e);
} catch (final PermissionDeniedException e) {
logger.error("{}: permission denied to read resource", path, e);
throw new XPathException(this, path + ": permission denied to read resource");
} catch (final IOException | TransactionException e) {
logger.error("{}: I/O error while reading resource", path, e);
throw new XPathException(this, path + ": I/O error while reading resource", e);
}
}
use of org.exist.storage.txn.Txn in project exist by eXist-db.
the class EmbeddedXMLStreamReaderTest method assertNodesIn.
public void assertNodesIn(final NamedEvent[] expected, final Function<Document, NodeHandle> initialNodeFun, final Optional<Function<Document, NodeHandle>> containerFun) throws EXistException, PermissionDeniedException, IOException, XMLStreamException {
final BrokerPool pool = existEmbeddedServer.getBrokerPool();
try (final DBBroker broker = pool.get(Optional.of(pool.getSecurityManager().getSystemSubject()));
final Txn transaction = pool.getTransactionManager().beginTransaction()) {
try (final LockedDocument lockedDocument = broker.getXMLResource(TEST_MIXED_XML_COLLECTION.append(MIXED_XML_NAME), Lock.LockMode.WRITE_LOCK)) {
assertNotNull(lockedDocument);
final Document document = lockedDocument.getDocument();
assertNotNull(document);
final NodeHandle initialNode = initialNodeFun.apply(document);
final Optional<NodeHandle> maybeContainerNode = containerFun.map(f -> f.apply(document));
final IEmbeddedXMLStreamReader xmlStreamReader = broker.getXMLStreamReader(initialNode, false);
final NamedEvent[] actual = readAllEvents(maybeContainerNode, xmlStreamReader);
assertArrayEquals(formatExpectedActual(expected, actual), expected, actual);
}
transaction.commit();
}
}
use of org.exist.storage.txn.Txn in project exist by eXist-db.
the class EmbeddedXMLStreamReaderTest method cleanup.
@AfterClass
public static void cleanup() throws EXistException, PermissionDeniedException, IOException, TriggerException {
final BrokerPool pool = existEmbeddedServer.getBrokerPool();
try (final DBBroker broker = pool.get(Optional.of(pool.getSecurityManager().getSystemSubject()));
final Txn transaction = pool.getTransactionManager().beginTransaction()) {
deleteCollection(broker, transaction, TEST_MIXED_XML_COLLECTION);
transaction.commit();
}
}
use of org.exist.storage.txn.Txn in project exist by eXist-db.
the class AbstractUpdateTest method init.
protected DocumentImpl init(final DBBroker broker, final TransactionManager transact) throws PermissionDeniedException, IOException, SAXException, LockException, EXistException {
DocumentImpl doc = null;
try (final Txn transaction = transact.beginTransaction()) {
final Collection root = broker.getOrCreateCollection(transaction, TEST_COLLECTION_URI);
broker.saveCollection(transaction, root);
final Collection test = broker.getOrCreateCollection(transaction, TEST_COLLECTION_URI.append("test2"));
broker.saveCollection(transaction, test);
broker.storeDocument(transaction, XmldbURI.create("test.xml"), new StringInputSource(TEST_XML), MimeType.XML_TYPE, test);
doc = test.getDocument(broker, XmldbURI.create("test.xml"));
// TODO : unlock the collection here ?
transact.commit(transaction);
}
return doc;
}
Aggregations