use of org.exist.collections.Collection in project exist by eXist-db.
the class NativeBroker method storeTempResource.
/**
* Store into the temporary collection of the database a given in-memory Document
*
* The in-memory Document is stored without a transaction and is not journalled,
* if there is no temporary collection, this will first be created with a transaction
*
* @param doc The in-memory Document to store
* @return The document stored in the temp collection
*/
@Override
public DocumentImpl storeTempResource(final org.exist.dom.memtree.DocumentImpl doc) throws EXistException, PermissionDeniedException, LockException {
try {
// elevate getUser() to DBA_USER
pushSubject(pool.getSecurityManager().getSystemSubject());
// start a transaction
final TransactionManager transact = pool.getTransactionManager();
// create a name for the temporary document
final XmldbURI docName = XmldbURI.create(MessageDigester.md5(Thread.currentThread().getName() + System.currentTimeMillis(), false) + ".xml");
// get the temp collection
try (final Txn transaction = transact.beginTransaction();
final ManagedCollectionLock tempCollectionLock = lockManager.acquireCollectionWriteLock(XmldbURI.TEMP_COLLECTION_URI)) {
// if temp collection does not exist, creates temp collection (with write lock in Txn)
final Tuple2<Boolean, Collection> createdOrExistingTemp = getOrCreateTempCollection(transaction);
if (createdOrExistingTemp == null) {
LOG.error("Failed to create temporary collection");
transact.abort(transaction);
return null;
}
final Collection temp = createdOrExistingTemp._2;
// create a temporary document
try (final ManagedDocumentLock docLock = lockManager.acquireDocumentWriteLock(temp.getURI().append(docName))) {
final int tmpDocId = getNextResourceId(transaction);
final Permission permission = PermissionFactory.getDefaultResourcePermission(getBrokerPool().getSecurityManager());
permission.setMode(Permission.DEFAULT_TEMPORARY_DOCUMENT_PERM);
final DocumentImpl targetDoc = new DocumentImpl(pool, temp, tmpDocId, docName, permission, 0, null, System.currentTimeMillis(), null, null, null);
// index the temporary document
final DOMIndexer indexer = new DOMIndexer(this, transaction, doc, targetDoc);
indexer.scan();
indexer.store();
// store the temporary document
temp.addDocument(transaction, this, targetDoc);
storeXMLResource(transaction, targetDoc);
saveCollection(transaction, temp);
// NOTE: early release of Collection lock inline with Asymmetrical Locking scheme
temp.close();
flush();
closeDocument();
// commit the transaction
transact.commit(transaction);
return targetDoc;
}
} catch (final Exception e) {
LOG.error("Failed to store temporary fragment: {}", e.getMessage(), e);
}
} finally {
// restore the user
popSubject();
}
return null;
}
use of org.exist.collections.Collection in project exist by eXist-db.
the class NativeBroker method reindexCollection.
private void reindexCollection(final Txn transaction, @EnsureLocked(mode = LockMode.READ_LOCK) final Collection collection, final IndexMode mode) throws PermissionDeniedException, IOException, LockException {
if (!collection.getPermissionsNoLock().validate(getCurrentSubject(), Permission.WRITE)) {
throw new PermissionDeniedException("Account " + getCurrentSubject().getName() + " have insufficient privileges on collection " + collection.getURI());
}
LOG.debug("Reindexing collection {}", collection.getURI());
if (mode == IndexMode.STORE) {
dropCollectionIndex(transaction, collection, true);
}
// reindex documents
try {
for (final Iterator<DocumentImpl> i = collection.iterator(this); i.hasNext(); ) {
final DocumentImpl next = i.next();
reindexXMLResource(transaction, next, mode);
}
} catch (final LockException e) {
LOG.error("LockException while reindexing documents of collection '{}'. Skipping...", collection.getURI(), e);
}
// descend into child collections
try {
for (final Iterator<XmldbURI> i = collection.collectionIterator(this); i.hasNext(); ) {
final XmldbURI childName = i.next();
final XmldbURI childUri = collection.getURI().append(childName);
try (final Collection child = openCollection(childUri, LockMode.READ_LOCK)) {
if (child == null) {
throw new IOException("Collection '" + childUri + "' not found");
} else {
reindexCollection(transaction, child, mode);
}
}
}
} catch (final LockException e) {
LOG.error("LockException while reindexing child collections of collection '{}'. Skipping...", collection.getURI(), e);
}
}
use of org.exist.collections.Collection in project exist by eXist-db.
the class NativeBroker method _removeCollection.
private boolean _removeCollection(final Txn transaction, @EnsureLocked(mode = LockMode.WRITE_LOCK) final Collection collection) throws PermissionDeniedException, TriggerException, IOException {
final XmldbURI collectionUri = collection.getURI();
getBrokerPool().getProcessMonitor().startJob(ProcessMonitor.ACTION_REMOVE_COLLECTION, collectionUri);
try {
// NOTE: we already have a WRITE lock on the parent of the Collection we set out to remove
@Nullable final Collection parentCollection = collection.getParentURI() == null ? null : getCollection(collection.getParentURI());
// TODO(AR) the below permissions check could be optimised when descending the tree so we don't check the same collection(s) twice in some cases
if (!checkRemoveCollectionPermissions(parentCollection, collection)) {
throw new PermissionDeniedException("Account '" + getCurrentSubject().getName() + "' is not allowed to remove collection '" + collection.getURI() + "'");
}
final CollectionTrigger colTrigger = new CollectionTriggers(this, transaction, parentCollection == null ? collection : parentCollection);
colTrigger.beforeDeleteCollection(this, transaction, collection);
// 2) remove descendant collections
for (final Iterator<XmldbURI> subCollectionName = collection.collectionIteratorNoLock(this); subCollectionName.hasNext(); ) {
// NOTE: we already have a WRITE lock on the parent of the Collection we set out to remove
final XmldbURI subCollectionUri = collectionUri.append(subCollectionName.next());
// NOTE: we already have a WRITE lock on the parent of the Collection we set out to remove
final boolean removedSubCollection = _removeCollection(transaction, getCollection(subCollectionUri));
if (!removedSubCollection) {
LOG.error("Unable to remove Collection: {}", subCollectionUri);
return false;
}
}
// TODO(AR) this can be executed asynchronously as a task, Do we need to await the completion before unlocking the collection? or just await completion before returning from the first call to _removeCollection?
// 3) drop indexes for this Collection
notifyDropIndex(collection);
getIndexController().removeCollection(collection, this, false);
// 4) remove this Collection from the parent Collection
if (parentCollection != null) {
parentCollection.removeCollection(this, collectionUri.lastSegment());
saveCollection(transaction, parentCollection);
}
// 5) remove Collection from collections.dbx
if (parentCollection != null) {
try (final ManagedLock<ReentrantLock> collectionsDbLock = lockManager.acquireBtreeWriteLock(collectionsDb.getLockName())) {
final Value key = new CollectionStore.CollectionKey(collectionUri.getRawCollectionPath());
collectionsDb.remove(transaction, key);
// TODO(AR) is this the correct place to invalidate the config?
// Notify the collection configuration manager
final CollectionConfigurationManager manager = pool.getConfigurationManager();
if (manager != null) {
manager.invalidate(collectionUri, getBrokerPool());
}
}
// invalidate the cache entry
final CollectionCache collectionsCache = pool.getCollectionsCache();
collectionsCache.invalidate(collection.getURI());
} else {
// if this is the root collection we just have to save
// it to persist the removal of any subCollections to collections.dbx
saveCollection(transaction, collection);
}
// 6) unlink all documents from the Collection
try (final ManagedLock<ReentrantLock> collectionsDbLock = lockManager.acquireBtreeWriteLock(collectionsDb.getLockName())) {
final Value docKey = new CollectionStore.DocumentKey(collection.getId());
final IndexQuery query = new IndexQuery(IndexQuery.TRUNC_RIGHT, docKey);
collectionsDb.removeAll(transaction, query);
if (parentCollection != null) {
// we must not free the root collection id!
collectionsDb.freeCollectionId(collection.getId());
}
} catch (final BTreeException | IOException e) {
LOG.error("Unable to unlink documents from the Collection: {}", collectionUri, e);
}
// TODO(AR) this can be executed asynchronously as a task, we need to await the completion before unlocking the collection
// 7) remove the documents nodes and binary documents of the Collection from dom.dbx
removeCollectionsDocumentNodes(transaction, collection);
colTrigger.afterDeleteCollection(this, transaction, collectionUri);
return true;
} catch (final LockException e) {
LOG.error("Unable to lock Collection: {}", collectionUri, e);
return false;
} finally {
getBrokerPool().getProcessMonitor().endJob();
}
}
use of org.exist.collections.Collection in project exist by eXist-db.
the class Deployment method scanDirectory.
private List<String> scanDirectory(final DBBroker broker, final Txn transaction, final Path directory, final XmldbURI target, final InMemoryNodeSet resources, final boolean inRootDir, final boolean isResourcesDir, final Optional<RequestedPerms> requestedPerms, final List<String> errors) {
Collection collection = null;
try {
collection = broker.getOrCreateCollection(transaction, target);
setPermissions(broker, requestedPerms, true, null, collection.getPermissionsNoLock());
broker.saveCollection(transaction, collection);
} catch (final PermissionDeniedException | TriggerException | IOException e) {
LOG.warn(e);
errors.add(e.getMessage());
}
final boolean isResources = isResourcesDir || isResourceDir(target, resources);
// the root dir is not allowed to be a resources directory
if (!inRootDir && isResources) {
try {
storeBinaryResources(broker, transaction, directory, collection, requestedPerms, errors);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
} else {
storeFiles(broker, transaction, directory, collection, inRootDir, requestedPerms, errors);
}
// scan sub directories
try (final Stream<Path> subDirs = Files.find(directory, 1, (path, attrs) -> (!path.equals(directory)) && attrs.isDirectory())) {
subDirs.forEach(path -> scanDirectory(broker, transaction, path, target.append(FileUtils.fileName(path)), resources, false, isResources, requestedPerms, errors));
} catch (final IOException ioe) {
LOG.warn("Unable to scan sub-directories", ioe);
}
return errors;
}
use of org.exist.collections.Collection in project exist by eXist-db.
the class Deployment method deploy.
public Optional<String> deploy(final DBBroker broker, final Txn transaction, final String pkgName, final Optional<ExistRepository> repo, final String userTarget) throws PackageException, IOException {
final Optional<Path> maybePackageDir = getPackageDir(pkgName, repo);
if (!maybePackageDir.isPresent()) {
throw new PackageException("Package not found: " + pkgName);
}
final Path packageDir = maybePackageDir.get();
final DocumentImpl repoXML = getRepoXML(broker, packageDir);
if (repoXML == null) {
return Optional.empty();
}
try {
// if there's a <setup> element, run the query it points to
final Optional<ElementImpl> setup = findElement(repoXML, SETUP_ELEMENT);
final Optional<String> setupPath = setup.map(ElementImpl::getStringValue).filter(s -> !s.isEmpty());
if (setupPath.isPresent()) {
runQuery(broker, null, packageDir, setupPath.get(), pkgName, QueryPurpose.SETUP);
return Optional.empty();
} else {
// otherwise create the target collection
XmldbURI targetCollection = null;
if (userTarget != null) {
try {
targetCollection = XmldbURI.create(userTarget);
} catch (final IllegalArgumentException e) {
throw new PackageException("Bad collection URI: " + userTarget, e);
}
} else {
final Optional<ElementImpl> target = findElement(repoXML, TARGET_COLL_ELEMENT);
final Optional<String> targetPath = target.map(ElementImpl::getStringValue).filter(s -> !s.isEmpty());
if (targetPath.isPresent()) {
// determine target collection
try {
targetCollection = XmldbURI.create(getTargetCollection(broker, targetPath.get()));
} catch (final IllegalArgumentException e) {
throw new PackageException("Bad collection URI for <target> element: " + targetPath.get(), e);
}
} else {
LOG.warn("EXPath Package '{}' does not contain a <target> in its repo.xml, no files will be deployed to /apps", pkgName);
}
}
if (targetCollection == null) {
// no target means: package does not need to be deployed into database
// however, we need to preserve a copy for backup purposes
final Optional<Package> pkg = getPackage(pkgName, repo);
pkg.orElseThrow(() -> new XPathException("expath repository is not available so the package was not stored."));
final String pkgColl = pkg.get().getAbbrev() + "-" + pkg.get().getVersion();
targetCollection = XmldbURI.SYSTEM.append("repo/" + pkgColl);
}
// extract the permissions (if any)
final Optional<ElementImpl> permissions = findElement(repoXML, PERMISSIONS_ELEMENT);
final Optional<RequestedPerms> requestedPerms = permissions.flatMap(elem -> {
final Optional<Either<Integer, String>> perms = Optional.ofNullable(elem.getAttribute("mode")).flatMap(mode -> {
try {
return Optional.of(Either.Left(Integer.parseInt(mode, 8)));
} catch (final NumberFormatException e) {
if (mode.matches("^[rwx-]{9}")) {
return Optional.of(Either.Right(mode));
} else {
return Optional.empty();
}
}
});
return perms.map(p -> new RequestedPerms(elem.getAttribute("user"), elem.getAttribute("password"), Optional.ofNullable(elem.getAttribute("group")), p));
});
// check that if there were permissions then we were able to parse them, a failure would be related to the mode string
if (permissions.isPresent() && !requestedPerms.isPresent()) {
final String mode = permissions.map(elem -> elem.getAttribute("mode")).orElse(null);
throw new PackageException("Bad format for mode attribute in <permissions>: " + mode);
}
// run the pre-setup query if present
final Optional<ElementImpl> preSetup = findElement(repoXML, PRE_SETUP_ELEMENT);
final Optional<String> preSetupPath = preSetup.map(ElementImpl::getStringValue).filter(s -> !s.isEmpty());
if (preSetupPath.isPresent()) {
runQuery(broker, targetCollection, packageDir, preSetupPath.get(), pkgName, QueryPurpose.PREINSTALL);
}
// TODO: if the user already exists, check and ensure the user is assigned to the specified group
if (requestedPerms.isPresent()) {
checkUserSettings(broker, requestedPerms.get());
}
final InMemoryNodeSet resources = findElements(repoXML, RESOURCES_ELEMENT);
// store all package contents into database, using the user/group/mode in the permissions element. however:
// 1. repo.xml is excluded for now, since it may contain the default user's password in the clear
// 2. contents of directories identified in the path attribute of any <resource path=""/> element are stored as binary
final List<String> errors = scanDirectory(broker, transaction, packageDir, targetCollection, resources, true, false, requestedPerms);
// store repo.xml, filtering out the default user's password
storeRepoXML(broker, transaction, repoXML, targetCollection, requestedPerms);
// run the post-setup query if present
final Optional<ElementImpl> postSetup = findElement(repoXML, POST_SETUP_ELEMENT);
final Optional<String> postSetupPath = postSetup.map(ElementImpl::getStringValue).filter(s -> !s.isEmpty());
if (postSetupPath.isPresent()) {
runQuery(broker, targetCollection, packageDir, postSetupPath.get(), pkgName, QueryPurpose.POSTINSTALL);
}
if (!errors.isEmpty()) {
throw new PackageException("Deployment incomplete, " + errors.size() + " issues found: " + String.join("; ", errors));
}
return Optional.ofNullable(targetCollection.getCollectionPath());
}
} catch (final XPathException e) {
throw new PackageException("Error found while processing repo.xml: " + e.getMessage(), e);
}
}
Aggregations