Search in sources :

Example 16 with Collection

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;
}
Also used : Txn(org.exist.storage.txn.Txn) TerminatedException(org.exist.xquery.TerminatedException) XMLStreamException(javax.xml.stream.XMLStreamException) SAXException(org.xml.sax.SAXException) EXistException(org.exist.EXistException) TransactionException(org.exist.storage.txn.TransactionException) TransactionManager(org.exist.storage.txn.TransactionManager) Collection(org.exist.collections.Collection) DOMIndexer(org.exist.dom.memtree.DOMIndexer) XmldbURI(org.exist.xmldb.XmldbURI)

Example 17 with Collection

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);
    }
}
Also used : Collection(org.exist.collections.Collection) XmldbURI(org.exist.xmldb.XmldbURI)

Example 18 with Collection

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();
    }
}
Also used : ReentrantLock(java.util.concurrent.locks.ReentrantLock) Collection(org.exist.collections.Collection) XmldbURI(org.exist.xmldb.XmldbURI) Nullable(javax.annotation.Nullable)

Example 19 with Collection

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;
}
Also used : Path(java.nio.file.Path) Collection(org.exist.collections.Collection) PermissionDeniedException(org.exist.security.PermissionDeniedException) IOException(java.io.IOException) TriggerException(org.exist.collections.triggers.TriggerException) PermissionDeniedException(org.exist.security.PermissionDeniedException) EXistException(org.exist.EXistException) IOException(java.io.IOException) SAXException(org.xml.sax.SAXException) TriggerException(org.exist.collections.triggers.TriggerException)

Example 20 with Collection

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);
    }
}
Also used : Path(java.nio.file.Path) DependencyVersion(org.expath.pkg.repo.deps.DependencyVersion) Txn(org.exist.storage.txn.Txn) java.util(java.util) BufferedInputStream(java.io.BufferedInputStream) QName(org.exist.dom.QName) SequenceIterator(org.exist.xquery.value.SequenceIterator) PermissionDeniedException(org.exist.security.PermissionDeniedException) org.exist.xquery(org.exist.xquery) DirectoryStream(java.nio.file.DirectoryStream) JarEntry(java.util.jar.JarEntry) org.exist.dom.memtree(org.exist.dom.memtree) Collection(org.exist.collections.Collection) UnixStylePermission(org.exist.security.UnixStylePermission) XmldbURI(org.exist.xmldb.XmldbURI) Attributes(org.xml.sax.Attributes) JarInputStream(java.util.jar.JarInputStream) EXistException(org.exist.EXistException) DocUtils(org.exist.xquery.util.DocUtils) DateTimeValue(org.exist.xquery.value.DateTimeValue) SystemProperties(org.exist.SystemProperties) Path(java.nio.file.Path) Permission(org.exist.security.Permission) Nullable(javax.annotation.Nullable) BatchUserInteraction(org.expath.pkg.repo.tui.BatchUserInteraction) PermissionFactory(org.exist.security.PermissionFactory) InputSource(org.xml.sax.InputSource) Files(java.nio.file.Files) GroupAider(org.exist.security.internal.aider.GroupAider) Type(org.exist.xquery.value.Type) FileSource(org.exist.source.FileSource) IOException(java.io.IOException) UserAider(org.exist.security.internal.aider.UserAider) Either(com.evolvedbinary.j8fu.Either) org.expath.pkg.repo(org.expath.pkg.repo) Logger(org.apache.logging.log4j.Logger) Element(org.w3c.dom.Element) Stream(java.util.stream.Stream) DBBroker(org.exist.storage.DBBroker) SAXException(org.xml.sax.SAXException) org.exist.util(org.exist.util) Sequence(org.exist.xquery.value.Sequence) TriggerException(org.exist.collections.triggers.TriggerException) LogManager(org.apache.logging.log4j.LogManager) Package(org.expath.pkg.repo.Package) AttrList(org.exist.util.serializer.AttrList) InputStream(java.io.InputStream) Either(com.evolvedbinary.j8fu.Either) Package(org.expath.pkg.repo.Package) XmldbURI(org.exist.xmldb.XmldbURI)

Aggregations

Collection (org.exist.collections.Collection)297 Txn (org.exist.storage.txn.Txn)160 XmldbURI (org.exist.xmldb.XmldbURI)99 DBBroker (org.exist.storage.DBBroker)89 TransactionManager (org.exist.storage.txn.TransactionManager)86 BrokerPool (org.exist.storage.BrokerPool)69 StringInputSource (org.exist.util.StringInputSource)57 Test (org.junit.Test)57 EXistException (org.exist.EXistException)43 PermissionDeniedException (org.exist.security.PermissionDeniedException)43 DocumentImpl (org.exist.dom.persistent.DocumentImpl)42 IOException (java.io.IOException)33 LockedDocument (org.exist.dom.persistent.LockedDocument)31 SAXException (org.xml.sax.SAXException)26 InputStream (java.io.InputStream)19 Path (java.nio.file.Path)19 Permission (org.exist.security.Permission)19 LockException (org.exist.util.LockException)16 TriggerException (org.exist.collections.triggers.TriggerException)15 BinaryDocument (org.exist.dom.persistent.BinaryDocument)15