Search in sources :

Example 1 with Package

use of org.expath.pkg.repo.Package in project exist by eXist-db.

the class AppRestoreUtils method checkApps.

/**
 * Inspects the apps contained in the backup against installed apps in the database
 * and return a set of symbolic backup paths pointing to the collection of those
 * apps for which newer versions are installed within the database. The returned
 * paths may then be ignored during a restore.
 *
 * The method attempts to be fail safe to make sure even bad backups can be restored. Errors
 * reading package descriptors are thus only logged and should not abort the process.
 *
 * @param broker the broker used for reading the backup and retrieving the expath repo
 * @param descriptors a queue of backup descriptors to inspect
 * @return a set of paths for which newer versions exist in the database. may be empty.
 */
public static Set<String> checkApps(final DBBroker broker, final Deque<BackupDescriptor> descriptors) {
    final List<AppDetail> apps = getAppsFromBackup(broker, descriptors);
    final Set<String> paths = new HashSet<>();
    final Optional<ExistRepository> repo = broker.getBrokerPool().getExpathRepo();
    if (repo.isPresent()) {
        for (final AppDetail app : apps) {
            final Packages packages = repo.get().getParentRepo().getPackages(app.name);
            if (packages != null) {
                final Package latest = packages.latest();
                try {
                    final Semver version = Semver.parse(latest.getVersion());
                    if (version.compareTo(app.version) > 0) {
                        paths.add(app.path);
                    }
                } catch (PackageException e) {
                    LOG.warn("Invalid semver in expath repository for {}", app.name, e);
                }
            }
        }
    }
    return paths;
}
Also used : Packages(org.expath.pkg.repo.Packages) PackageException(org.expath.pkg.repo.PackageException) Package(org.expath.pkg.repo.Package) Semver(org.expath.pkg.repo.deps.Semver) ExistRepository(org.exist.repo.ExistRepository)

Example 2 with Package

use of org.expath.pkg.repo.Package in project exist by eXist-db.

the class Deployment method cleanup.

/**
 * After deployment, clean up the package directory and remove all files which have been
 * stored into the db. They are not needed anymore. Only preserve the descriptors and the
 * contents directory.
 *
 * @param pkgName
 * @param repo
 * @throws PackageException
 */
private void cleanup(final String pkgName, final Optional<ExistRepository> repo) throws PackageException {
    if (repo.isPresent()) {
        final Optional<Package> pkg = getPackage(pkgName, repo);
        final Optional<Path> maybePackageDir = pkg.map(this::getPackageDir);
        if (!maybePackageDir.isPresent()) {
            throw new PackageException("Cleanup: package dir for package " + pkgName + " not found");
        }
        final Path packageDir = maybePackageDir.get();
        final String abbrev = pkg.get().getAbbrev();
        try (final Stream<Path> filesToDelete = Files.find(packageDir, 1, (path, attrs) -> {
            if (path.equals(packageDir)) {
                return false;
            }
            final String name = FileUtils.fileName(path);
            if (attrs.isDirectory()) {
                return !(name.equals(abbrev) || name.equals("content"));
            } else {
                return !(name.equals("expath-pkg.xml") || name.equals("repo.xml") || "exist.xml".equals(name) || name.startsWith("icon"));
            }
        })) {
            filesToDelete.forEach(path -> {
                try {
                    Files.deleteIfExists(path);
                } catch (final IOException ioe) {
                    LOG.warn("Cleanup: failed to delete file {} in package {}", path.toAbsolutePath().toString(), pkgName);
                }
            });
        } catch (final IOException ioe) {
            LOG.warn("Cleanup: failed to delete files", ioe);
        }
    }
}
Also used : Path(java.nio.file.Path) Package(org.expath.pkg.repo.Package) IOException(java.io.IOException)

Example 3 with Package

use of org.expath.pkg.repo.Package 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)

Example 4 with Package

use of org.expath.pkg.repo.Package in project exist by eXist-db.

the class Deployment method installAndDeploy.

/**
 * Install and deploy a give xar archive. Dependencies are installed from
 * the PackageLoader.
 *
 * @param broker the broker to use
 * @param transaction the transaction for this deployment task
 * @param xar the .xar file to install
 * @param loader package loader to use
 * @param enforceDeps when set to true, the method will throw an exception if a dependency could not be resolved
 *                    or an older version of the required dependency is installed and needs to be replaced.
 * @return the collection path to which the package was deployed or Optional.empty if not deployed
 * @throws PackageException if package installation failed
 * @throws IOException in case of an IO error
 */
public Optional<String> installAndDeploy(final DBBroker broker, final Txn transaction, final XarSource xar, final PackageLoader loader, boolean enforceDeps) throws PackageException, IOException {
    final Optional<DocumentImpl> descriptor = getDescriptor(broker, xar);
    if (!descriptor.isPresent()) {
        throw new PackageException("Missing descriptor from package: " + xar.getURI());
    }
    final DocumentImpl document = descriptor.get();
    final ElementImpl root = (ElementImpl) document.getDocumentElement();
    final String name = root.getAttribute("name");
    final String pkgVersion = root.getAttribute("version");
    final Optional<ExistRepository> repo = broker.getBrokerPool().getExpathRepo();
    if (repo.isPresent()) {
        final Packages packages = repo.get().getParentRepo().getPackages(name);
        if (packages != null && (!enforceDeps || pkgVersion.equals(packages.latest().getVersion()))) {
            LOG.info("Application package {} already installed. Skipping.", name);
            final Package pkg = packages.latest();
            return Optional.of(getTargetCollection(broker, pkg, getPackageDir(pkg)));
        }
        InMemoryNodeSet deps;
        try {
            deps = findElements(root, DEPENDENCY_ELEMENT);
            for (final SequenceIterator i = deps.iterate(); i.hasNext(); ) {
                final Element dependency = (Element) i.nextItem();
                final String pkgName = dependency.getAttribute("package");
                final String processor = dependency.getAttribute("processor");
                final String versionStr = dependency.getAttribute("version");
                final String semVer = dependency.getAttribute("semver");
                final String semVerMin = dependency.getAttribute("semver-min");
                final String semVerMax = dependency.getAttribute("semver-max");
                PackageLoader.Version version = null;
                if (semVer != null) {
                    version = new PackageLoader.Version(semVer, true);
                } else if (semVerMax != null || semVerMin != null) {
                    version = new PackageLoader.Version(semVerMin, semVerMax);
                } else if (pkgVersion != null) {
                    version = new PackageLoader.Version(versionStr, false);
                }
                if (processor != null && processor.equals(PROCESSOR_NAME) && version != null) {
                    checkProcessorVersion(version);
                } else if (pkgName != null) {
                    LOG.info("Package {} depends on {}", name, pkgName);
                    boolean isInstalled = false;
                    if (repo.get().getParentRepo().getPackages(pkgName) != null) {
                        LOG.debug("Package {} already installed", pkgName);
                        Packages pkgs = repo.get().getParentRepo().getPackages(pkgName);
                        // check if installed package matches required version
                        if (pkgs != null) {
                            if (version != null) {
                                Package latest = pkgs.latest();
                                DependencyVersion depVersion = version.getDependencyVersion();
                                if (depVersion.isCompatible(latest.getVersion())) {
                                    isInstalled = true;
                                } else {
                                    LOG.debug("Package {} needs to be upgraded", pkgName);
                                    if (enforceDeps) {
                                        throw new PackageException("Package requires version " + version.toString() + " of package " + pkgName + ". Installed version is " + latest.getVersion() + ". Please upgrade!");
                                    }
                                }
                            } else {
                                isInstalled = true;
                            }
                            if (isInstalled) {
                                LOG.debug("Package {} already installed", pkgName);
                            }
                        }
                    }
                    if (!isInstalled && loader != null) {
                        final XarSource depFile = loader.load(pkgName, version);
                        if (depFile != null) {
                            installAndDeploy(broker, transaction, depFile, loader);
                        } else {
                            if (enforceDeps) {
                                LOG.warn("Missing dependency: package {} could not be resolved. This error is not fatal, but the package may not work as expected", pkgName);
                            } else {
                                throw new PackageException("Missing dependency: package " + pkgName + " could not be resolved.");
                            }
                        }
                    }
                }
            }
        } catch (final XPathException e) {
            throw new PackageException("Invalid descriptor found in " + xar.getURI());
        }
        // installing the xar into the expath repo
        LOG.info("Installing package {}", xar.getURI());
        final UserInteractionStrategy interact = new BatchUserInteraction();
        final org.expath.pkg.repo.Package pkg = repo.get().getParentRepo().installPackage(xar, true, interact);
        final ExistPkgInfo info = (ExistPkgInfo) pkg.getInfo("exist");
        if (info != null && !info.getJars().isEmpty()) {
            ClasspathHelper.updateClasspath(broker.getBrokerPool(), pkg);
        }
        broker.getBrokerPool().getXQueryPool().clear();
        final String pkgName = pkg.getName();
        // signal status
        broker.getBrokerPool().reportStatus("Installing app: " + pkg.getAbbrev());
        repo.get().reportAction(ExistRepository.Action.INSTALL, pkg.getName());
        LOG.info("Deploying package {}", pkgName);
        return deploy(broker, transaction, pkgName, repo, null);
    }
    // Totally unnecessary to do the above if repo is unavailable.
    return Optional.empty();
}
Also used : Element(org.w3c.dom.Element) org.expath.pkg.repo(org.expath.pkg.repo) SequenceIterator(org.exist.xquery.value.SequenceIterator) DependencyVersion(org.expath.pkg.repo.deps.DependencyVersion) DependencyVersion(org.expath.pkg.repo.deps.DependencyVersion) Package(org.expath.pkg.repo.Package) Package(org.expath.pkg.repo.Package) BatchUserInteraction(org.expath.pkg.repo.tui.BatchUserInteraction)

Example 5 with Package

use of org.expath.pkg.repo.Package in project exist by eXist-db.

the class ExistRepository method resolveXSLTModule.

public Source resolveXSLTModule(final String namespace) throws PackageException {
    for (final Packages pp : myParent.listPackages()) {
        final Package pkg = pp.latest();
        final Source src = pkg.resolve(namespace, URISpace.XSLT);
        if (src != null) {
            return src;
        }
    }
    return null;
}
Also used : Package(org.expath.pkg.repo.Package) StreamSource(javax.xml.transform.stream.StreamSource) Source(javax.xml.transform.Source)

Aggregations

Package (org.expath.pkg.repo.Package)13 IOException (java.io.IOException)6 Path (java.nio.file.Path)5 BatchUserInteraction (org.expath.pkg.repo.tui.BatchUserInteraction)5 URI (java.net.URI)4 Txn (org.exist.storage.txn.Txn)4 Either (com.evolvedbinary.j8fu.Either)3 BufferedInputStream (java.io.BufferedInputStream)3 InputStream (java.io.InputStream)3 DirectoryStream (java.nio.file.DirectoryStream)3 Files (java.nio.file.Files)3 java.util (java.util)3 JarEntry (java.util.jar.JarEntry)3 JarInputStream (java.util.jar.JarInputStream)3 Stream (java.util.stream.Stream)3 Nullable (javax.annotation.Nullable)3 LogManager (org.apache.logging.log4j.LogManager)3 Logger (org.apache.logging.log4j.Logger)3 EXistException (org.exist.EXistException)3 SystemProperties (org.exist.SystemProperties)3