use of org.exist.xmldb.XmldbURI 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);
}
}
use of org.exist.xmldb.XmldbURI in project exist by eXist-db.
the class EmbeddedOutputStream method uploadToDb.
private static void uploadToDb(final BrokerPool pool, final XmldbURL url, final Path tempFile) throws IOException {
try (final DBBroker broker = pool.getBroker()) {
final XmldbURI collectionUri = XmldbURI.create(url.getCollection());
final XmldbURI documentUri = XmldbURI.create(url.getDocumentName());
try (final Collection collection = broker.openCollection(collectionUri, Lock.LockMode.WRITE_LOCK)) {
if (collection == null) {
throw new IOException("Resource " + collectionUri.toString() + " is not a collection.");
}
if (collection.hasChildCollection(broker, documentUri)) {
throw new IOException("Resource " + documentUri.toString() + " is a collection.");
}
final MimeType mime = MimeTable.getInstance().getContentTypeFor(documentUri);
final TransactionManager transact = pool.getTransactionManager();
try (final Txn txn = transact.beginTransaction()) {
broker.storeDocument(txn, documentUri, new FileInputSource(tempFile), mime, collection);
txn.commit();
}
}
} catch (final EXistException | PermissionDeniedException | LockException | SAXException e) {
LOG.error(e);
throw new IOException(e.getMessage(), e);
} finally {
if (LOG.isDebugEnabled()) {
LOG.debug("End document upload");
}
}
}
use of org.exist.xmldb.XmldbURI in project exist by eXist-db.
the class NativeBroker method reindexCollection.
@Override
public void reindexCollection(final Txn transaction, final XmldbURI collectionUri) throws PermissionDeniedException, IOException, LockException {
if (isReadOnly()) {
throw new IOException(DATABASE_IS_READ_ONLY);
}
final XmldbURI fqUri = prepend(collectionUri.toCollectionPathURI());
final long start = System.currentTimeMillis();
try (final Collection collection = openCollection(fqUri, LockMode.READ_LOCK)) {
if (collection == null) {
LOG.warn("Collection {} not found!", fqUri);
return;
}
LOG.info("Start indexing collection {}", collection.getURI().toString());
pool.getProcessMonitor().startJob(ProcessMonitor.ACTION_REINDEX_COLLECTION, collection.getURI());
reindexCollection(transaction, collection, IndexMode.STORE);
} catch (final PermissionDeniedException | IOException e) {
LOG.error("An error occurred during reindex: {}", e.getMessage(), e);
} finally {
pool.getProcessMonitor().endJob();
LOG.info("Finished indexing collection {} in {} ms.", fqUri, System.currentTimeMillis() - start);
}
}
use of org.exist.xmldb.XmldbURI in project exist by eXist-db.
the class NativeBroker method lockDescendantDocuments.
/**
* Acquires locks on all descendant Collections of a specific Collection
*
* Locks are acquired in a top-down, left-to-right order
*
* NOTE: It is assumed that the caller holds a lock on the
* `collection` of the same mode as those that we should acquire on the descendants
*
* @param collection The Collection whose descendant locks should be acquired
* @param lockFn A function for acquiring a lock
*
* @return A list of locks in the same order as collectionUris. Note that these should be released in reverse order
*/
private List<ManagedDocumentLock> lockDescendantDocuments(final Collection collection, final FunctionE<XmldbURI, ManagedDocumentLock, LockException> lockFn) throws LockException, PermissionDeniedException {
final List<ManagedDocumentLock> locks = new ArrayList<>();
try {
final Iterator<DocumentImpl> itDoc = collection.iteratorNoLock(this);
while (itDoc.hasNext()) {
final DocumentImpl doc = itDoc.next();
final ManagedDocumentLock docLock = lockFn.apply(doc.getURI());
locks.add(docLock);
}
final XmldbURI collectionUri = collection.getURI();
// NOTE: we should already have a lock on collection
final Iterator<XmldbURI> it = collection.collectionIteratorNoLock(this);
while (it.hasNext()) {
final XmldbURI childCollectionName = it.next();
final XmldbURI childCollectionUri = collectionUri.append(childCollectionName);
// NOTE: we don't need to lock the collection as we should already implicitly have a lock on the collection sub-tree
final Collection childCollection = getCollection(childCollectionUri);
final List<ManagedDocumentLock> descendantLocks = lockDescendantDocuments(childCollection, lockFn);
locks.addAll(descendantLocks);
}
} catch (final PermissionDeniedException | LockException e) {
// unlock in reverse order
try {
ManagedLocks.closeAll(locks);
} catch (final RuntimeException re) {
LOG.error(re);
}
throw e;
}
return locks;
}
use of org.exist.xmldb.XmldbURI in project exist by eXist-db.
the class NativeBroker method copyResource.
@Override
public void copyResource(final Txn transaction, final DocumentImpl sourceDocument, final Collection targetCollection, final XmldbURI newDocName, final PreserveType preserve) throws PermissionDeniedException, LockException, IOException, TriggerException, EXistException {
assert (sourceDocument != null);
assert (targetCollection != null);
assert (newDocName != null);
if (isReadOnly()) {
throw new IOException(DATABASE_IS_READ_ONLY);
}
if (newDocName.numSegments() != 1) {
throw new IOException("newName name must be just a name i.e. an XmldbURI with one segment!");
}
final XmldbURI sourceDocumentUri = sourceDocument.getURI();
final XmldbURI targetCollectionUri = targetCollection.getURI();
final XmldbURI targetDocumentUri = targetCollectionUri.append(newDocName);
if (!sourceDocument.getPermissions().validate(getCurrentSubject(), Permission.READ)) {
throw new PermissionDeniedException("Account '" + getCurrentSubject().getName() + "' has insufficient privileges to copy the resource '" + sourceDocumentUri + "'.");
}
// we assume the caller holds a READ_LOCK (or better) on sourceDocument#getCollection()
final Collection sourceCollection = sourceDocument.getCollection();
if (!sourceCollection.getPermissions().validate(getCurrentSubject(), Permission.EXECUTE)) {
throw new PermissionDeniedException("Account '" + getCurrentSubject().getName() + "' has insufficient privileges to copy the resource '" + sourceDocumentUri + "'.");
}
if (!targetCollection.getPermissionsNoLock().validate(getCurrentSubject(), Permission.EXECUTE)) {
throw new PermissionDeniedException("Account '" + getCurrentSubject().getName() + "' does not have execute access on the destination collection '" + targetCollectionUri + "'.");
}
if (targetCollection.hasChildCollection(this, newDocName.lastSegment())) {
throw new EXistException("The collection '" + targetCollectionUri + "' already has a sub-collection named '" + newDocName.lastSegment() + "', you cannot create a Document with the same name as an existing collection.");
}
try (final LockedDocument oldLockedDoc = targetCollection.getDocumentWithLock(this, newDocName, LockMode.WRITE_LOCK)) {
final DocumentTrigger trigger = new DocumentTriggers(this, transaction, targetCollection);
final DocumentImpl oldDoc = oldLockedDoc == null ? null : oldLockedDoc.getDocument();
if (oldDoc == null) {
if (!targetCollection.getPermissionsNoLock().validate(getCurrentSubject(), Permission.WRITE)) {
throw new PermissionDeniedException("Account '" + getCurrentSubject().getName() + "' does not have write access on the destination collection '" + targetCollectionUri + "'.");
}
} else {
if (sourceDocument.getDocId() == oldDoc.getDocId()) {
throw new PermissionDeniedException("Cannot copy resource to itself '" + sourceDocumentUri + "'.");
}
if (!oldDoc.getPermissions().validate(getCurrentSubject(), Permission.WRITE)) {
throw new PermissionDeniedException("A resource with the same name already exists in the target collection '" + oldDoc.getURI() + "', and you do not have write access on that resource.");
}
trigger.beforeDeleteDocument(this, transaction, oldDoc);
trigger.afterDeleteDocument(this, transaction, targetDocumentUri);
}
doCopyDocument(transaction, trigger, sourceDocument, targetCollection, newDocName, oldDoc, preserve);
}
}
Aggregations