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;
}
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);
}
}
}
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);
}
}
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();
}
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;
}
Aggregations