/* * (C) Copyright 2012-2016 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Julien Carsique * Mathieu Guillaume * Yannis JULIENNE * */ package org.nuxeo.launcher.connect; import java.io.Console; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.ListUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.impl.SimpleLog; import org.nuxeo.common.Environment; import org.nuxeo.connect.CallbackHolder; import org.nuxeo.connect.NuxeoConnectClient; import org.nuxeo.connect.connector.ConnectServerError; import org.nuxeo.connect.data.DownloadablePackage; import org.nuxeo.connect.data.DownloadingPackage; import org.nuxeo.connect.identity.LogicalInstanceIdentifier; import org.nuxeo.connect.identity.LogicalInstanceIdentifier.InvalidCLID; import org.nuxeo.connect.identity.LogicalInstanceIdentifier.NoCLID; import org.nuxeo.connect.packages.PackageManager; import org.nuxeo.connect.packages.dependencies.CUDFHelper; import org.nuxeo.connect.packages.dependencies.DependencyResolution; import org.nuxeo.connect.update.LocalPackage; import org.nuxeo.connect.update.Package; import org.nuxeo.connect.update.PackageException; import org.nuxeo.connect.update.PackageState; import org.nuxeo.connect.update.PackageType; import org.nuxeo.connect.update.PackageUtils; import org.nuxeo.connect.update.PackageVisibility; import org.nuxeo.connect.update.ValidationStatus; import org.nuxeo.connect.update.Version; import org.nuxeo.connect.update.model.PackageDefinition; import org.nuxeo.connect.update.standalone.StandaloneUpdateService; import org.nuxeo.connect.update.task.Task; import org.nuxeo.launcher.info.CommandInfo; import org.nuxeo.launcher.info.CommandSetInfo; import org.nuxeo.launcher.info.PackageInfo; import org.w3c.dom.Document; import org.w3c.dom.NodeList; /** * @since 5.6 */ public class ConnectBroker { private static final Log log = LogFactory.getLog(ConnectBroker.class); public static final String PARAM_MP_DIR = "nuxeo.distribution.marketplace.dir"; public static final String DISTRIBUTION_MP_DIR_DEFAULT = "setupWizardDownloads"; public static final String PACKAGES_XML = "packages.xml"; protected static final String LAUNCHER_CHANGED_PROPERTY = "launcher.changed"; protected static final int LAUNCHER_CHANGED_EXIT_CODE = 128; public static final String[] POSITIVE_ANSWERS = { "true", "yes", "y" }; private Environment env; private StandaloneUpdateService service; private CallbackHolder cbHolder; private CommandSetInfo cset = new CommandSetInfo(); private String targetPlatform; private String distributionMPDir; private String relax = OPTION_RELAX_DEFAULT; public static final String OPTION_RELAX_DEFAULT = "ask"; private String accept = OPTION_ACCEPT_DEFAULT; private boolean allowSNAPSHOT = CUDFHelper.defaultAllowSNAPSHOT; public static final String OPTION_ACCEPT_DEFAULT = "ask"; public ConnectBroker(Environment env) throws IOException, PackageException { this.env = env; service = new StandaloneUpdateService(env); service.initialize(); cbHolder = new StandaloneCallbackHolder(env, service); NuxeoConnectClient.setCallBackHolder(cbHolder); targetPlatform = env.getProperty(Environment.DISTRIBUTION_NAME) + "-" + env.getProperty(Environment.DISTRIBUTION_VERSION); distributionMPDir = env.getProperty(PARAM_MP_DIR, DISTRIBUTION_MP_DIR_DEFAULT); } public String getCLID() throws NoCLID { return LogicalInstanceIdentifier.instance().getCLID(); } /** * @throws NoCLID * @since 6.0 */ public void setCLID(String file) throws NoCLID { try { LogicalInstanceIdentifier.load(file); } catch (IOException | InvalidCLID e) { throw new NoCLID("can not load CLID", e); } } public StandaloneUpdateService getUpdateService() { return service; } public PackageManager getPackageManager() { return NuxeoConnectClient.getPackageManager(); } public void refreshCache() { getPackageManager().flushCache(); NuxeoConnectClient.getPackageManager().listAllPackages(); } public CommandSetInfo getCommandSet() { return cset; } protected LocalPackage getInstalledPackageByName(String pkgName) { try { return service.getPersistence().getActivePackage(pkgName); } catch (PackageException e) { log.error(e); return null; } } protected boolean isInstalledPackage(String pkgName) { try { return service.getPersistence().getActivePackageId(pkgName) != null; } catch (PackageException e) { log.error("Error checking installation of package " + pkgName, e); return false; } } protected boolean isLocalPackageId(String pkgId) { try { return service.getPackage(pkgId) != null; } catch (PackageException e) { log.error("Error looking for local package " + pkgId, e); return false; } } protected boolean isRemotePackageId(String pkgId) { return PackageUtils.isValidPackageId(pkgId) && NuxeoConnectClient.getPackageManager().findPackageById(pkgId) != null; } protected String getBestIdForNameInList(String pkgName, List<? extends Package> pkgList) { String foundId = null; SortedMap<Version, String> foundPkgs = new TreeMap<>(); SortedMap<Version, String> matchingPkgs = new TreeMap<>(); for (Package pkg : pkgList) { if (pkg.getName().equals(pkgName)) { foundPkgs.put(pkg.getVersion(), pkg.getId()); if (Arrays.asList(pkg.getTargetPlatforms()).contains(targetPlatform)) { matchingPkgs.put(pkg.getVersion(), pkg.getId()); } } } if (matchingPkgs.size() != 0) { foundId = matchingPkgs.get(matchingPkgs.lastKey()); } else if (foundPkgs.size() != 0) { foundId = foundPkgs.get(foundPkgs.lastKey()); } return foundId; } protected String getLocalPackageIdFromName(String pkgName) { return getBestIdForNameInList(pkgName, getPkgList()); } protected List<String> getAllLocalPackageIdsFromName(String pkgName) { List<String> foundIds = new ArrayList<>(); for (Package pkg : getPkgList()) { if (pkg.getName().equals(pkgName)) { foundIds.add(pkg.getId()); } } return foundIds; } protected String getInstalledPackageIdFromName(String pkgName) { List<LocalPackage> localPackages = getPkgList(); List<LocalPackage> installedPackages = new ArrayList<>(); for (LocalPackage pkg : localPackages) { if (pkg.getPackageState().isInstalled()) { installedPackages.add(pkg); } } return getBestIdForNameInList(pkgName, installedPackages); } protected String getRemotePackageIdFromName(String pkgName) { return getBestIdForNameInList(pkgName, NuxeoConnectClient.getPackageManager().findRemotePackages(pkgName)); } /** * Looks for a remote package from its name or id * * @param pkgNameOrId * @return the remote package Id; null if not found * @since 5.7 */ protected String getRemotePackageId(String pkgNameOrId) { String pkgId = null; if (isRemotePackageId(pkgNameOrId)) { pkgId = pkgNameOrId; } else { pkgId = getRemotePackageIdFromName(pkgNameOrId); } return pkgId; } /** * Looks for a local package from its name or id * * @since 5.7 * @param pkgIdOrName * @return the local package Id; null if not found * @throws PackageException */ protected LocalPackage getLocalPackage(String pkgIdOrName) throws PackageException { // Try as a package id LocalPackage pkg = service.getPackage(pkgIdOrName); if (pkg == null) { // Check whether this is the name of a local package String pkgId = getLocalPackageIdFromName(pkgIdOrName); if (pkgId != null) { pkg = service.getPackage(pkgId); } } return pkg; } /** * Looks for a package file from its path * * @param pkgFile Absolute or relative package file path * @return the file if found, else null */ protected File getLocalPackageFile(String pkgFile) { if (pkgFile.startsWith("file:")) { pkgFile = pkgFile.substring(5); } // Try absolute path File fileToCheck = new File(pkgFile); if (!fileToCheck.exists()) { // Try relative path fileToCheck = new File(env.getServerHome(), pkgFile); } if (fileToCheck.exists()) { return fileToCheck; } else { return null; } } /** * Load package definition from a local file or directory and get package Id from it. * * @return null the package definition cannot be loaded for any reason. * @since 8.4 */ protected String getLocalPackageFileId(File pkgFile) { PackageDefinition packageDefinition = null; try { if (pkgFile.isFile()) { packageDefinition = service.loadPackageFromZip(pkgFile); } else if (pkgFile.isDirectory()) { File manifest = new File(pkgFile, LocalPackage.MANIFEST); packageDefinition = service.loadPackage(manifest); } else { throw new PackageException("Unknown file type (not a file and not a directory) for " + pkgFile); } } catch (PackageException e) { log.error("Error trying to load package id from " + pkgFile, e); return null; } return packageDefinition == null ? null : packageDefinition.getId(); } protected boolean isLocalPackageFile(String pkgFile) { return (getLocalPackageFile(pkgFile) != null); } protected List<String> getDistributionFilenames() { File distributionMPFile = new File(distributionMPDir, PACKAGES_XML); List<String> md5Filenames = new ArrayList<>(); // Try to get md5 files from packages.xml DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); docFactory.setNamespaceAware(true); try { DocumentBuilder builder = docFactory.newDocumentBuilder(); Document doc = builder.parse(distributionMPFile); XPathFactory xpFactory = XPathFactory.newInstance(); XPath xpath = xpFactory.newXPath(); XPathExpression expr = xpath.compile("//package/@md5"); NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { String md5 = nodes.item(i).getNodeValue(); if ((md5 != null) && (md5.length() > 0)) { md5Filenames.add(md5); } } } catch (Exception e) { // Parsing failed - return empty list log.error("Failed parsing " + distributionMPFile, e); return new ArrayList<>(); } return md5Filenames; } protected Map<String, PackageDefinition> getDistributionDefinitions(List<String> md5Filenames) { Map<String, PackageDefinition> allDefinitions = new HashMap<>(); if (md5Filenames == null) { return allDefinitions; } for (String md5Filename : md5Filenames) { File md5File = new File(distributionMPDir, md5Filename); if (!md5File.exists()) { // distribution file has been deleted continue; } ZipFile zipFile; try { zipFile = new ZipFile(md5File); } catch (ZipException e) { log.warn("Unzip error reading file " + md5File, e); continue; } catch (IOException e) { log.warn("Could not read file " + md5File, e); continue; } try { ZipEntry zipEntry = zipFile.getEntry("package.xml"); InputStream in = zipFile.getInputStream(zipEntry); PackageDefinition pd = NuxeoConnectClient.getPackageUpdateService().loadPackage(in); allDefinitions.put(md5Filename, pd); } catch (Exception e) { log.error("Could not read package description", e); continue; } finally { try { zipFile.close(); } catch (IOException e) { log.warn("Unexpected error closing file " + md5File, e); } } } return allDefinitions; } protected boolean addDistributionPackage(String md5) { boolean ret = true; File distributionFile = new File(distributionMPDir, md5); if (distributionFile.exists()) { try { ret = pkgAdd(distributionFile.getCanonicalPath(), false) != null; } catch (IOException e) { log.warn("Could not add distribution file " + md5); ret = false; } } return ret; } public boolean addDistributionPackages() { Map<String, PackageDefinition> distributionPackages = getDistributionDefinitions(getDistributionFilenames()); if (distributionPackages.isEmpty()) { return true; } List<LocalPackage> localPackages = getPkgList(); Map<String, LocalPackage> localPackagesById = new HashMap<>(); if (localPackages != null) { for (LocalPackage pkg : localPackages) { localPackagesById.put(pkg.getId(), pkg); } } boolean ret = true; for (String md5 : distributionPackages.keySet()) { PackageDefinition md5Pkg = distributionPackages.get(md5); if (localPackagesById.containsKey(md5Pkg.getId())) { // We have the same package Id in the local cache LocalPackage localPackage = localPackagesById.get(md5Pkg.getId()); if (localPackage.getVersion().isSnapshot()) { // - For snapshots, until we have timestamp support, assume // distribution version is newer than cached version. // - This may (will) break the server if there are // dependencies/compatibility changes or if the package is // in installed state. if (!localPackage.getPackageState().isInstalled()) { pkgRemove(localPackage.getId()); ret = addDistributionPackage(md5) && ret; } } } else { // No package with this Id is in cache ret = addDistributionPackage(md5) && ret; } } return ret; } public List<LocalPackage> getPkgList() { try { return service.getPackages(); } catch (PackageException e) { log.error("Could not read package list", e); return null; } } public void pkgList() { log.info("Local packages:"); pkgList(getPkgList()); } public void pkgListAll() { log.info("All packages:"); pkgList(NuxeoConnectClient.getPackageManager().listAllPackages()); } public void pkgList(List<? extends Package> packagesList) { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_LIST); try { if (packagesList.isEmpty()) { log.info("None"); } else { NuxeoConnectClient.getPackageManager().sort(packagesList); StringBuilder sb = new StringBuilder(); for (Package pkg : packagesList) { newPackageInfo(cmdInfo, pkg); PackageState packageState = pkg.getPackageState(); String packageDescription = packageState.getLabel(); packageDescription = String.format("%6s %11s\t", pkg.getType(), packageDescription); if (packageState == PackageState.REMOTE && pkg.getType() != PackageType.STUDIO && pkg.getVisibility() != PackageVisibility.PUBLIC && !LogicalInstanceIdentifier.isRegistered()) { packageDescription += "Registration required for "; } packageDescription += String.format("%s (id: %s)\n", pkg.getName(), pkg.getId()); sb.append(packageDescription); } log.info(sb.toString()); } } catch (Exception e) { log.error(e); cmdInfo.exitCode = 1; } } protected void performTask(Task task) throws PackageException { ValidationStatus validationStatus = task.validate(); if (validationStatus.hasErrors()) { throw new PackageException( "Failed to validate package " + task.getPackage().getId() + " -> " + validationStatus.getErrors()); } if (validationStatus.hasWarnings()) { log.warn("Got warnings on package validation " + task.getPackage().getId() + " -> " + validationStatus.getWarnings()); } task.run(null); } public boolean pkgReset() { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_RESET); if ("ask".equalsIgnoreCase(accept)) { accept = readConsole( "The reset will erase the Nuxeo Packages history.\n" + "Do you want to continue (yes/no)? [yes] ", "yes"); } if (!Boolean.parseBoolean(accept)) { cmdInfo.exitCode = 1; return false; } try { service.reset(); log.info("Packages reset done: all packages were marked as DOWNLOADED"); List<LocalPackage> localPackages = service.getPackages(); for (LocalPackage localPackage : localPackages) { localPackage.getUninstallFile().delete(); FileUtils.deleteDirectory(localPackage.getData().getEntry(LocalPackage.BACKUP_DIR)); newPackageInfo(cmdInfo, localPackage); } service.getRegistry().delete(); FileUtils.deleteDirectory(service.getBackupDir()); } catch (PackageException e) { log.error(e); cmdInfo.exitCode = 1; } catch (IOException e) { log.error(e); cmdInfo.exitCode = 1; } return cmdInfo.exitCode == 0; } public boolean pkgPurge() throws PackageException { List<String> localNames = new ArrayList<>(); // Remove packages in DOWNLOADED state first // This will avoid extending the CUDF universe needlessly for (LocalPackage pkg : service.getPackages()) { if (pkg.getPackageState() == PackageState.DOWNLOADED) { pkgRemove(pkg.getId()); } } // Process the remaining packages for (LocalPackage pkg : service.getPackages()) { localNames.add(pkg.getName()); } return pkgRequest(null, null, null, localNames, true, false); } /** * Uninstall a list of packages. If the list contains a package name (versus an ID), only the considered as best * matching package is uninstalled. * * @param packageIdsToRemove The list can contain package IDs and names * @see #pkgUninstall(String) */ public boolean pkgUninstall(List<String> packageIdsToRemove) { log.debug("Uninstalling: " + packageIdsToRemove); for (String pkgId : packageIdsToRemove) { if (pkgUninstall(pkgId) == null) { log.error("Unable to uninstall " + pkgId); return false; } } return true; } /** * Uninstall a local package. The package is not removed from cache. * * @param pkgId Package ID or Name * @return The uninstalled LocalPackage or null if failed */ public LocalPackage pkgUninstall(String pkgId) { if (env.getProperty(LAUNCHER_CHANGED_PROPERTY, "false").equals("true")) { System.exit(LAUNCHER_CHANGED_EXIT_CODE); } CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_UNINSTALL); cmdInfo.param = pkgId; try { LocalPackage pkg = service.getPackage(pkgId); if (pkg == null) { // Check whether this is the name of an installed package String realPkgId = getInstalledPackageIdFromName(pkgId); if (realPkgId != null) { pkgId = realPkgId; pkg = service.getPackage(realPkgId); } } if (pkg == null) { throw new PackageException("Package not found: " + pkgId); } log.info("Uninstalling " + pkgId); Task uninstallTask = pkg.getUninstallTask(); try { performTask(uninstallTask); } catch (PackageException e) { uninstallTask.rollback(); throw e; } // Refresh state pkg = service.getPackage(pkgId); newPackageInfo(cmdInfo, pkg); return pkg; } catch (Exception e) { log.error("Failed to uninstall package: " + pkgId, e); cmdInfo.exitCode = 1; return null; } } /** * Remove a list of packages from cache. If the list contains a package name (versus an ID), all matching packages * are removed. * * @param pkgsToRemove The list can contain package IDs and names * @see #pkgRemove(String) */ public boolean pkgRemove(List<String> pkgsToRemove) { boolean cmdOk = true; if (pkgsToRemove != null) { log.debug("Removing: " + pkgsToRemove); for (String pkgNameOrId : pkgsToRemove) { List<String> allIds; if (isLocalPackageId(pkgNameOrId)) { allIds = new ArrayList<>(); allIds.add(pkgNameOrId); } else { // Request made on a name: remove all matching packages allIds = getAllLocalPackageIdsFromName(pkgNameOrId); } for (String pkgId : allIds) { if (pkgRemove(pkgId) == null) { log.warn("Unable to remove " + pkgId); // Don't error out on failed (cache) removal cmdOk = false; } } } } return cmdOk; } /** * Remove a package from cache. If it was installed, the package is uninstalled then removed. * * @param pkgId Package ID or Name * @return The removed LocalPackage or null if failed */ public LocalPackage pkgRemove(String pkgId) { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_REMOVE); cmdInfo.param = pkgId; try { LocalPackage pkg = service.getPackage(pkgId); if (pkg == null) { // Check whether this is the name of a local package String realPkgId = getLocalPackageIdFromName(pkgId); if (realPkgId != null) { pkgId = realPkgId; pkg = service.getPackage(realPkgId); } } if (pkg == null) { throw new PackageException("Package not found: " + pkgId); } if (pkg.getPackageState().isInstalled()) { pkgUninstall(pkgId); // Refresh state pkg = service.getPackage(pkgId); } if (pkg.getPackageState() != PackageState.DOWNLOADED) { throw new PackageException("Can only remove packages in DOWNLOADED, INSTALLED or STARTED state"); } service.removePackage(pkgId); log.info("Removed " + pkgId); newPackageInfo(cmdInfo, pkg).state = PackageState.REMOTE; return pkg; } catch (Exception e) { log.error("Failed to remove package: " + pkgId, e); cmdInfo.exitCode = 1; return null; } } /** * Add a list of packages into the cache, downloading them if needed and possible. * * @param pkgsToAdd * @return true if command succeeded * @see #pkgAdd(List, boolean) * @see #pkgAdd(String, boolean) * @deprecated Since 7.10. Use a method with an explicit value for {@code ignoreMissing}. */ @Deprecated public boolean pkgAdd(List<String> pkgsToAdd) { return pkgAdd(pkgsToAdd, false); } /** * Add a list of packages into the cache, downloading them if needed and possible. * * @since 6.0 * @param pkgsToAdd * @param ignoreMissing * @return true if command succeeded * @see #pkgAdd(String, boolean) */ public boolean pkgAdd(List<String> pkgsToAdd, boolean ignoreMissing) { boolean cmdOk = true; if (pkgsToAdd == null || pkgsToAdd.isEmpty()) { return cmdOk; } List<String> pkgIdsToDownload = new ArrayList<>(); for (String pkgToAdd : pkgsToAdd) { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_ADD); cmdInfo.param = pkgToAdd; try { File fileToAdd = getLocalPackageFile(pkgToAdd); if (fileToAdd == null) { String pkgId = getRemotePackageId(pkgToAdd); if (pkgId == null) { if (ignoreMissing) { log.warn("Could not add package: " + pkgToAdd); cmdInfo.newMessage(SimpleLog.LOG_LEVEL_INFO, "Could not add package."); } else { throw new PackageException("Could not find a remote or local (relative to " + "current directory or to NUXEO_HOME) " + "package with name or ID " + pkgToAdd); } } else { cmdInfo.newMessage(SimpleLog.LOG_LEVEL_INFO, "Waiting for download..."); pkgIdsToDownload.add(pkgId); } } else { LocalPackage pkg = service.addPackage(fileToAdd); log.info("Added " + pkg); newPackageInfo(cmdInfo, pkg); } } catch (PackageException e) { cmdOk = false; cmdInfo.exitCode = 1; cmdInfo.newMessage(e); } } cmdOk = downloadPackages(pkgIdsToDownload) && cmdOk; return cmdOk; } /** * Add a package file into the cache * * @param packageFileName * @return The added LocalPackage or null if failed * @see #pkgAdd(List, boolean) * @see #pkgAdd(String, boolean) * @deprecated Since 7.10. Use a method with an explicit value for {@code ignoreMissing}. */ @Deprecated public LocalPackage pkgAdd(String packageFileName) { return pkgAdd(packageFileName, false); } /** * Add a package file into the cache * * @since 6.0 * @param packageFileName * @param ignoreMissing * @return The added LocalPackage or null if failed * @see #pkgAdd(List, boolean) */ public LocalPackage pkgAdd(String packageFileName, boolean ignoreMissing) { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_ADD); cmdInfo.param = packageFileName; LocalPackage pkg = null; try { File fileToAdd = getLocalPackageFile(packageFileName); if (fileToAdd == null) { String pkgId = getRemotePackageId(packageFileName); if (pkgId == null) { if (ignoreMissing) { log.warn("Could not add package: " + packageFileName); cmdInfo.newMessage(SimpleLog.LOG_LEVEL_INFO, "Could not add package."); return null; } else { throw new PackageException("Could not find a remote or local (relative to " + "current directory or to NUXEO_HOME) " + "package with name or ID " + packageFileName); } } else if (!downloadPackages(Arrays.asList(new String[] { pkgId }))) { throw new PackageException("Could not download package " + pkgId); } pkg = service.getPackage(pkgId); if (pkg == null) { throw new PackageException("Could not find downloaded package in cache " + pkgId); } } else { pkg = service.addPackage(fileToAdd); log.info("Added " + packageFileName); } newPackageInfo(cmdInfo, pkg); } catch (PackageException e) { cmdInfo.exitCode = 1; cmdInfo.newMessage(e); } return pkg; } /** * Install a list of local packages. If the list contains a package name (versus an ID), only the considered as best * matching package is installed. * * @param packageIdsToInstall The list can contain package IDs and names * @see #pkgInstall(List, boolean) * @see #pkgInstall(String, boolean) * @deprecated Since 7.10. Use a method with an explicit value for {@code ignoreMissing}. */ @Deprecated public boolean pkgInstall(List<String> packageIdsToInstall) { return pkgInstall(packageIdsToInstall, false); } /** * Install a list of local packages. If the list contains a package name (versus an ID), only the considered as best * matching package is installed. * * @since 6.0 * @param packageIdsToInstall The list can contain package IDs and names * @param ignoreMissing If true, doesn't throw an exception on unknown packages * @see #pkgInstall(String, boolean) */ public boolean pkgInstall(List<String> packageIdsToInstall, boolean ignoreMissing) { log.debug("Installing: " + packageIdsToInstall); for (String pkgId : packageIdsToInstall) { if (pkgInstall(pkgId, ignoreMissing) == null && !ignoreMissing) { return false; } } return true; } /** * Install a local package. * * @param pkgId Package ID or Name * @return The installed LocalPackage or null if failed * @see #pkgInstall(List, boolean) * @see #pkgInstall(String, boolean) * @deprecated Since 7.10. Use a method with an explicit value for {@code ignoreMissing}. */ @Deprecated public LocalPackage pkgInstall(String pkgId) { return pkgInstall(pkgId, false); } /** * Install a local package. * * @since 6.0 * @param pkgId Package ID or Name * @param ignoreMissing If true, doesn't throw an exception on unknown packages * @return The installed LocalPackage or null if failed * @see #pkgInstall(List, boolean) */ public LocalPackage pkgInstall(String pkgId, boolean ignoreMissing) { if (env.getProperty(LAUNCHER_CHANGED_PROPERTY, "false").equals("true")) { System.exit(LAUNCHER_CHANGED_EXIT_CODE); } CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_INSTALL); cmdInfo.param = pkgId; try { LocalPackage pkg = getLocalPackage(pkgId); if (pkg != null && pkg.getPackageState().isInstalled()) { if (pkg.getVersion().isSnapshot()) { log.info(String.format("Updating package %s...", pkg)); // First remove it to allow SNAPSHOT upgrade pkgRemove(pkgId); pkg = null; } else { log.info(String.format("Package %s is already installed.", pkg)); return pkg; } } if (pkg == null) { // We don't know this package, try to add it first pkg = pkgAdd(pkgId, ignoreMissing); } if (pkg == null) { // Nothing worked - can't find the package anywhere if (ignoreMissing) { log.warn("Unable to install package: " + pkgId); return null; } else { throw new PackageException("Package not found: " + pkgId); } } pkgId = pkg.getId(); cmdInfo.param = pkgId; log.info("Installing " + pkgId); Task installTask = pkg.getInstallTask(); try { performTask(installTask); } catch (PackageException e) { installTask.rollback(); throw e; } // Refresh state pkg = service.getPackage(pkgId); newPackageInfo(cmdInfo, pkg); return pkg; } catch (PackageException e) { log.error(String.format("Failed to install package: %s (%s)", pkgId, e.getMessage())); log.debug(e, e); cmdInfo.exitCode = 1; cmdInfo.newMessage(e); return null; } } public boolean listPending(File commandsFile) { return executePending(commandsFile, false, false, false); } /** * @since 5.6 * @param commandsFile File containing the commands to execute * @param doExecute Whether to execute or list the actions * @param useResolver Whether to use full resolution or just execute individual actions */ public boolean executePending(File commandsFile, boolean doExecute, boolean useResolver, boolean ignoreMissing) { int errorValue = 0; if (!commandsFile.isFile()) { return false; } List<String> pkgsToAdd = new ArrayList<>(); List<String> pkgsToInstall = new ArrayList<>(); List<String> pkgsToUninstall = new ArrayList<>(); List<String> pkgsToRemove = new ArrayList<>(); List<String> lines; try { lines = FileUtils.readLines(commandsFile); for (String line : lines) { line = line.trim(); String[] split = line.split("\\s+", 2); if (split.length == 2) { if (split[0].equals(CommandInfo.CMD_INSTALL)) { if (doExecute) { if (useResolver) { pkgsToInstall.add(split[1]); } else { pkgInstall(split[1], ignoreMissing); } } else { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_INSTALL); cmdInfo.param = split[1]; cmdInfo.pending = true; } } else if (split[0].equals(CommandInfo.CMD_ADD)) { if (doExecute) { if (useResolver) { pkgsToAdd.add(split[1]); } else { pkgAdd(split[1], ignoreMissing); } } else { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_ADD); cmdInfo.param = split[1]; cmdInfo.pending = true; } } else if (split[0].equals(CommandInfo.CMD_UNINSTALL)) { if (doExecute) { if (useResolver) { pkgsToUninstall.add(split[1]); } else { pkgUninstall(split[1]); } } else { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_UNINSTALL); cmdInfo.param = split[1]; cmdInfo.pending = true; } } else if (split[0].equals(CommandInfo.CMD_REMOVE)) { if (doExecute) { if (useResolver) { pkgsToRemove.add(split[1]); } else { pkgRemove(split[1]); } } else { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_REMOVE); cmdInfo.param = split[1]; cmdInfo.pending = true; } } else { errorValue = 1; } } else if (split.length == 1) { if (line.length() > 0 && !line.startsWith("#")) { if (doExecute) { if ("init".equals(line)) { if (!addDistributionPackages()) { errorValue = 1; } } else { if (useResolver) { pkgsToInstall.add(line); } else { pkgInstall(line, ignoreMissing); } } } else { if ("init".equals(line)) { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_INIT); cmdInfo.pending = true; } else { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_INSTALL); cmdInfo.param = line; cmdInfo.pending = true; } } } } if (errorValue != 0) { log.error("Error processing pending package/command: " + line); } } if (doExecute) { if (useResolver) { String oldAccept = accept; String oldRelax = relax; accept = "true"; if ("ask".equalsIgnoreCase(relax)) { log.info("Relax mode changed from 'ask' to 'false' for executing the pending actions."); relax = "false"; } boolean success = pkgRequest(pkgsToAdd, pkgsToInstall, pkgsToUninstall, pkgsToRemove, true, ignoreMissing); accept = oldAccept; relax = oldRelax; if (!success) { errorValue = 2; } } if (errorValue != 0) { File bak = new File(commandsFile.getPath() + ".bak"); bak.delete(); commandsFile.renameTo(bak); log.error("Pending actions execution failed. The commands file has been moved to: " + bak); } else { commandsFile.delete(); } } else { cset.log(true); } } catch (IOException e) { log.error(e.getMessage()); } return errorValue == 0; } @SuppressWarnings("unused") protected boolean downloadPackages(List<String> packagesToDownload) { boolean isRegistered = LogicalInstanceIdentifier.isRegistered(); List<String> packagesAlreadyDownloaded = new ArrayList<String>(); for (String pkg : packagesToDownload) { LocalPackage localPackage; try { localPackage = getLocalPackage(pkg); } catch (PackageException e) { log.error(String.format("Looking for package '%s' in local cache raised an error. Aborting.", pkg), e); return false; } if (localPackage == null) { continue; } if (localPackage.getPackageState().isInstalled()) { log.error(String.format("Package '%s' is installed. Download skipped.", pkg)); packagesAlreadyDownloaded.add(pkg); } else if (localPackage.getVersion().isSnapshot()) { if (localPackage.getVisibility() != PackageVisibility.PUBLIC && !isRegistered) { log.info(String.format("Update of '%s' requires being registered.", pkg)); packagesAlreadyDownloaded.add(pkg); } else { log.info(String.format("Download of '%s' will replace the one already in local cache.", pkg)); } } else { log.info(String.format("Package '%s' is already in local cache.", pkg)); packagesAlreadyDownloaded.add(pkg); } } packagesToDownload.removeAll(packagesAlreadyDownloaded); if (packagesToDownload.isEmpty()) { return true; } // Queue downloads log.info("Downloading " + packagesToDownload + "..."); boolean downloadOk = true; List<DownloadingPackage> pkgs = new ArrayList<DownloadingPackage>(); for (String pkg : packagesToDownload) { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_DOWNLOAD); cmdInfo.param = pkg; // Check registration and package visibility DownloadablePackage downloadablePkg = getPackageManager().findRemotePackageById(pkg); if (downloadablePkg.getVisibility() != PackageVisibility.PUBLIC && !isRegistered) { downloadOk = false; cmdInfo.exitCode = 1; cmdInfo.newMessage(SimpleLog.LOG_LEVEL_ERROR, "Registration required."); continue; } // Download try { DownloadingPackage download = getPackageManager().download(pkg); if (download != null) { pkgs.add(download); cmdInfo.param = download.getId(); cmdInfo.newMessage(SimpleLog.LOG_LEVEL_DEBUG, "Downloading..."); } else { downloadOk = false; cmdInfo.exitCode = 1; cmdInfo.newMessage(SimpleLog.LOG_LEVEL_ERROR, "Download failed (not found)."); } } catch (ConnectServerError e) { log.debug(e, e); downloadOk = false; cmdInfo.exitCode = 1; cmdInfo.newMessage(SimpleLog.LOG_LEVEL_ERROR, "Download failed: " + e.getMessage()); } } // Check and display progress final String progress = "|/-\\"; int x = 0; boolean stopDownload = false; do { System.out.print(progress.charAt(x++ % progress.length()) + "\r"); try { Thread.sleep(1000); } catch (InterruptedException e) { stopDownload = true; } List<DownloadingPackage> pkgsCompleted = new ArrayList<DownloadingPackage>(); for (DownloadingPackage pkg : pkgs) { if (pkg.isCompleted()) { pkgsCompleted.add(pkg); CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_DOWNLOAD); cmdInfo.param = pkg.getId(); // Digest check not correctly implemented if (false && !pkg.isDigestOk()) { downloadOk = false; cmdInfo.exitCode = 1; cmdInfo.newMessage(SimpleLog.LOG_LEVEL_ERROR, "Wrong digest."); } else if (pkg.getPackageState() == PackageState.DOWNLOADED) { cmdInfo.newMessage(SimpleLog.LOG_LEVEL_DEBUG, "Downloaded."); } else { downloadOk = false; cmdInfo.exitCode = 1; cmdInfo.newMessage(SimpleLog.LOG_LEVEL_ERROR, "Download failed: " + pkg.getErrorMessage()); if (pkg.isServerError()) { // Wasted effort to continue other downloads stopDownload = true; } } } } pkgs.removeAll(pkgsCompleted); } while (!stopDownload && pkgs.size() > 0); if (pkgs.size() > 0) { downloadOk = false; log.error("Packages download was interrupted"); for (DownloadingPackage pkg : pkgs) { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_ADD); cmdInfo.param = pkg.getId(); cmdInfo.exitCode = 1; cmdInfo.newMessage(SimpleLog.LOG_LEVEL_ERROR, "Download interrupted."); } } return downloadOk; } /** * @deprecated Since 7.10. Use {@link #pkgRequest(List, List, List, List, boolean, boolean)} instead. */ @Deprecated public boolean pkgRequest(List<String> pkgsToAdd, List<String> pkgsToInstall, List<String> pkgsToUninstall, List<String> pkgsToRemove) { return pkgRequest(pkgsToAdd, pkgsToInstall, pkgsToUninstall, pkgsToRemove, true, false); } /** * @param keepExisting If false, the request will remove existing packages that are not part of the resolution * @since 5.9.2 * @deprecated Since 7.10. Use {@link #pkgRequest(List, List, List, List, boolean, boolean)} instead. */ @Deprecated public boolean pkgRequest(List<String> pkgsToAdd, List<String> pkgsToInstall, List<String> pkgsToUninstall, List<String> pkgsToRemove, boolean keepExisting) { return pkgRequest(pkgsToAdd, pkgsToInstall, pkgsToUninstall, pkgsToRemove, keepExisting, false); } /** * @param keepExisting If false, the request will remove existing packages that are not part of the resolution * @param ignoreMissing Do not error out on missing packages, just handle the rest * @since 5.9.2 */ public boolean pkgRequest(List<String> pkgsToAdd, List<String> pkgsToInstall, List<String> pkgsToUninstall, List<String> pkgsToRemove, boolean keepExisting, boolean ignoreMissing) { // default is install mode return pkgRequest(pkgsToAdd, pkgsToInstall, pkgsToUninstall, pkgsToRemove, keepExisting, ignoreMissing, false); } /** * @param keepExisting If false, the request will remove existing packages that are not part of the resolution * @param ignoreMissing Do not error out on missing packages, just handle the rest * @param upgradeMode If true, all packages will be upgraded to their last compliant version * @since 8.4 */ public boolean pkgRequest(List<String> pkgsToAdd, List<String> pkgsToInstall, List<String> pkgsToUninstall, List<String> pkgsToRemove, boolean keepExisting, boolean ignoreMissing, boolean upgradeMode) { try { boolean cmdOk = true; // Add local files cmdOk = pkgAdd(pkgsToAdd, ignoreMissing); // Build solver request List<String> solverInstall = new ArrayList<>(); List<String> solverRemove = new ArrayList<>(); List<String> solverUpgrade = new ArrayList<>(); // Potential local cache snapshots to replace Set<String> localSnapshotsToMaybeReplace = new HashSet<>(); if (pkgsToInstall != null) { List<String> namesOrIdsToInstall = new ArrayList<>(); Set<String> localSnapshotsToUninstall = new HashSet<>(); Set<String> localSnapshotsToReplace = new HashSet<>(); cmdOk = checkLocalPackagesAndAddLocalFiles(pkgsToInstall, upgradeMode, ignoreMissing, namesOrIdsToInstall, localSnapshotsToUninstall, localSnapshotsToReplace, localSnapshotsToMaybeReplace); // Replace snapshots to install but already in cache (requested by id or filename) if (CollectionUtils.isNotEmpty(localSnapshotsToReplace)) { log.info(String.format("The following SNAPSHOT package(s) will be replaced in local cache : %s", localSnapshotsToReplace)); String initialAccept = accept; if ("ask".equalsIgnoreCase(accept)) { accept = readConsole("Do you want to continue (yes/no)? [yes] ", "yes"); } if (!Boolean.parseBoolean(accept)) { log.warn("Exit"); return false; } accept = initialAccept; for (String pkgId : localSnapshotsToUninstall) { LocalPackage uninstalledPkg = pkgUninstall(pkgId); if (uninstalledPkg == null) { cmdOk = false; } } for (String pkgId : localSnapshotsToReplace) { LocalPackage addedPkg = pkgAdd(pkgId, ignoreMissing); if (addedPkg == null) { cmdOk = false; } } } if (upgradeMode) { solverUpgrade.addAll(namesOrIdsToInstall); } else { solverInstall.addAll(namesOrIdsToInstall); } } if (pkgsToUninstall != null) { solverRemove.addAll(pkgsToUninstall); } if (pkgsToRemove != null) { // Add packages to remove to uninstall list solverRemove.addAll(pkgsToRemove); } if ((solverInstall.size() != 0) || (solverRemove.size() != 0) || (solverUpgrade.size() != 0)) { // Check whether we need to relax restriction to targetPlatform String requestPlatform = targetPlatform; List<String> requestPackages = new ArrayList<>(); requestPackages.addAll(solverInstall); requestPackages.addAll(solverRemove); requestPackages.addAll(solverUpgrade); if (ignoreMissing) { // Remove unknown packages from the list Map<String, List<DownloadablePackage>> knownNames = getPackageManager().getAllPackagesByName(); List<String> solverInstallCopy = new ArrayList<>(solverInstall); for (String pkgToInstall : solverInstallCopy) { if (!knownNames.containsKey(pkgToInstall)) { log.warn("Unable to install unknown package: " + pkgToInstall); solverInstall.remove(pkgToInstall); requestPackages.remove(pkgToInstall); } } } List<String> nonCompliantPkg = getPackageManager().getNonCompliantList(requestPackages, targetPlatform); if (nonCompliantPkg.size() > 0) { requestPlatform = null; if ("ask".equalsIgnoreCase(relax)) { relax = readConsole( "Package(s) %s not available on platform version %s.\n" + "Do you want to relax the constraint (yes/no)? [no] ", "no", StringUtils.join(nonCompliantPkg, ", "), targetPlatform); } if (Boolean.parseBoolean(relax)) { log.warn(String.format("Relax restriction to target platform %s because of package(s) %s", targetPlatform, StringUtils.join(nonCompliantPkg, ", "))); } else { if (ignoreMissing) { for (String pkgToInstall : nonCompliantPkg) { log.warn("Unable to install package: " + pkgToInstall); solverInstall.remove(pkgToInstall); } } else { throw new PackageException(String.format( "Package(s) %s not available on platform version %s (relax is not allowed)", StringUtils.join(nonCompliantPkg, ", "), targetPlatform)); } } } log.debug("solverInstall: " + solverInstall); log.debug("solverRemove: " + solverRemove); log.debug("solverUpgrade: " + solverUpgrade); DependencyResolution resolution = getPackageManager().resolveDependencies(solverInstall, solverRemove, solverUpgrade, requestPlatform, allowSNAPSHOT, keepExisting); log.info(resolution); if (resolution.isFailed()) { return false; } if (resolution.isEmpty()) { pkgRemove(pkgsToRemove); return cmdOk; } if ("ask".equalsIgnoreCase(accept)) { accept = readConsole("Do you want to continue (yes/no)? [yes] ", "yes"); } if (!Boolean.parseBoolean(accept)) { log.warn("Exit"); return false; } List<String> packageIdsToRemove = resolution.getOrderedPackageIdsToRemove(); List<String> packageIdsToUpgrade = resolution.getUpgradePackageIds(); List<String> packageIdsToInstall = resolution.getOrderedPackageIdsToInstall(); List<String> packagesIdsToReInstall = new ArrayList<>(); // Replace snapshots to install but already in cache (requested by name) if (CollectionUtils.containsAny(packageIdsToInstall, localSnapshotsToMaybeReplace)) { for (Object pkgIdObj : CollectionUtils.intersection(packageIdsToInstall, localSnapshotsToMaybeReplace)) { String pkgId = (String) pkgIdObj; LocalPackage addedPkg = pkgAdd(pkgId, ignoreMissing); if (addedPkg == null) { cmdOk = false; } } } // Download remote packages if (!downloadPackages(resolution.getDownloadPackageIds())) { log.error("Aborting packages change request"); return false; } // Uninstall if (!packageIdsToUpgrade.isEmpty()) { // Add packages to upgrade to uninstall list // Don't use IDs to avoid downgrade instead of uninstall packageIdsToRemove.addAll(resolution.getLocalPackagesToUpgrade().keySet()); DependencyResolution uninstallResolution = getPackageManager().resolveDependencies(null, packageIdsToRemove, null, requestPlatform, allowSNAPSHOT, keepExisting, true); log.debug("Sub-resolution (uninstall) " + uninstallResolution); if (uninstallResolution.isFailed()) { return false; } List<String> newPackageIdsToRemove = uninstallResolution.getOrderedPackageIdsToRemove(); packagesIdsToReInstall = ListUtils.subtract(newPackageIdsToRemove, packageIdsToRemove); packagesIdsToReInstall.removeAll(packageIdsToUpgrade); packageIdsToRemove = newPackageIdsToRemove; } if (!pkgUninstall(packageIdsToRemove)) { return false; } // Install if (!packagesIdsToReInstall.isEmpty()) { // Add list of packages uninstalled because of upgrade packageIdsToInstall.addAll(packagesIdsToReInstall); DependencyResolution installResolution = getPackageManager().resolveDependencies( packageIdsToInstall, null, null, requestPlatform, allowSNAPSHOT, keepExisting, true); log.debug("Sub-resolution (install) " + installResolution); if (installResolution.isFailed()) { return false; } packageIdsToInstall = installResolution.getOrderedPackageIdsToInstall(); } if (!pkgInstall(packageIdsToInstall, ignoreMissing)) { return false; } pkgRemove(pkgsToRemove); } return cmdOk; } catch (PackageException e) { log.error(e); log.debug(e, e); return false; } } private boolean checkLocalPackagesAndAddLocalFiles(List<String> pkgsToInstall, boolean upgradeMode, boolean ignoreMissing, List<String> namesOrIdsToInstall, Set<String> localSnapshotsToUninstall, Set<String> localSnapshotsToReplace, Set<String> localSnapshotsToMaybeReplace) throws PackageException { boolean cmdOk = true; for (String pkgToInstall : pkgsToInstall) { String nameOrIdToInstall = pkgToInstall; if (!upgradeMode) { boolean isLocalPackageFile = isLocalPackageFile(pkgToInstall); if (isLocalPackageFile) { // If install request is a file name, get the id nameOrIdToInstall = getLocalPackageFileId(getLocalPackageFile(pkgToInstall)); } // get corresponding local package if present. // if request is a name, prefer installed package LocalPackage localPackage = getInstalledPackageByName(nameOrIdToInstall); if (localPackage != null) { // as not in upgrade mode, replace the package name by the installed package id nameOrIdToInstall = localPackage.getId(); } else { if (isLocalPackageId(nameOrIdToInstall)) { // if request is an id, get potential package in local cache localPackage = getLocalPackage(nameOrIdToInstall); } else { // if request is a name but there is no installed package matching, get the best version // in local cache to replace it if it is a snapshot and it happens to be the actual // version to install afterward LocalPackage potentialMatchingPackage = getLocalPackage(nameOrIdToInstall); if (potentialMatchingPackage != null && potentialMatchingPackage.getVersion().isSnapshot()) { localSnapshotsToMaybeReplace.add(potentialMatchingPackage.getId()); } } } // first install of local file or directory if (localPackage == null && isLocalPackageFile) { LocalPackage addedPkg = pkgAdd(pkgToInstall, ignoreMissing); if (addedPkg == null) { cmdOk = false; } } // if a requested SNAPSHOT package is present, mark it for replacement in local cache if (localPackage != null && localPackage.getVersion().isSnapshot()) { if (localPackage.getPackageState().isInstalled()) { // if it's already installed, unintall it localSnapshotsToUninstall.add(nameOrIdToInstall); } // use the local file name if given and ensure we replace the right version, in case // nameOrIdToInstall is a name String pkgToAdd = isLocalPackageFile ? pkgToInstall : localPackage.getId(); localSnapshotsToReplace.add(pkgToAdd); } } namesOrIdsToInstall.add(nameOrIdToInstall); } return cmdOk; } /** * Installs a list of packages and uninstalls the rest (no dependency check) * * @since 5.9.2 * @deprecated Since 7.10. Use #pkgSet(List, boolean) instead. */ @Deprecated public boolean pkgSet(List<String> pkgList) { return pkgSet(pkgList, false); } /** * Installs a list of packages and uninstalls the rest (no dependency check) * * @since 6.0 */ public boolean pkgSet(List<String> pkgList, boolean ignoreMissing) { boolean cmdOK = true; cmdOK = cmdOK && pkgInstall(pkgList, ignoreMissing); List<DownloadablePackage> installedPkgs = getPackageManager().listInstalledPackages(); List<String> pkgsToUninstall = new ArrayList<>(); for (DownloadablePackage pkg : installedPkgs) { if ((!pkgList.contains(pkg.getName())) && (!pkgList.contains(pkg.getId()))) { pkgsToUninstall.add(pkg.getId()); } } if (pkgsToUninstall.size() != 0) { cmdOK = cmdOK && pkgUninstall(pkgsToUninstall); } return cmdOK; } /** * Prompt user for yes/no answer * * @param message The message to display * @param defaultValue The default answer if there's no console or if "Enter" key is pressed. * @param objects Parameters to use in the message (like in {@link String#format(String, Object...)}) * @return {@code "true"} if answer is in {@link #POSITIVE_ANSWERS}, else return {@code "false"} */ protected String readConsole(String message, String defaultValue, Object... objects) { String answer; Console console = System.console(); if (console == null || StringUtils.isEmpty(answer = console.readLine(message, objects))) { answer = defaultValue; } answer = answer.trim().toLowerCase(); return parseAnswer(answer); } /** * @return {@code "true"} if answer is in {@link #POSITIVE_ANSWERS}, and {@code "ask"} if answer values * {@code "ask"}, else return {@code "false"} * @since 6.0 */ public static String parseAnswer(String answer) { if ("ask".equalsIgnoreCase(answer)) { return "ask"; } if ("false".equalsIgnoreCase(answer)) { return "false"; } for (String positive : POSITIVE_ANSWERS) { if (positive.equalsIgnoreCase(answer)) { return "true"; } } return "false"; } public boolean pkgHotfix() { List<String> hotFixNames = getPackageManager().listHotfixesNames(targetPlatform, allowSNAPSHOT); return pkgRequest(null, hotFixNames, null, null, true, false); } public boolean pkgUpgrade() { List<String> upgradeNames = getPackageManager().listInstalledPackagesNames(null); // use upgrade mode return pkgRequest(null, upgradeNames, null, null, true, false, true); } /** * Must be called after {@link #setAccept(String)} which overwrites its value. * * @param relaxValue true, false or ask; ignored if null */ public void setRelax(String relaxValue) { if (relaxValue != null) { relax = parseAnswer(relaxValue); } } /** * @param acceptValue true, false or ask; if true or ask, then calls {@link #setRelax(String)} with the same value; * ignored if null */ public void setAccept(String acceptValue) { if (acceptValue != null) { accept = parseAnswer(acceptValue); if ("ask".equals(accept) || "true".equals(accept)) { setRelax(acceptValue); } } } /* * Helper for adding a new PackageInfo initialized with informations gathered from the given package. It is not put * into CommandInfo to avoid adding a dependency on Connect Client */ private PackageInfo newPackageInfo(CommandInfo cmdInfo, Package pkg) { PackageInfo packageInfo = new PackageInfo(pkg); cmdInfo.packages.add(packageInfo); return packageInfo; } /** * @param packages List of packages identified by their ID, name or local filename. * @since 5.7 */ public boolean pkgShow(List<String> packages) { boolean cmdOk = true; if (packages == null || packages.isEmpty()) { return cmdOk; } StringBuilder sb = new StringBuilder(); sb.append("****************************************"); for (String pkg : packages) { CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_SHOW); cmdInfo.param = pkg; try { PackageInfo packageInfo = newPackageInfo(cmdInfo, findPackage(pkg)); sb.append("\nPackage: " + packageInfo.id); sb.append("\nState: " + packageInfo.state); sb.append("\nVersion: " + packageInfo.version); sb.append("\nName: " + packageInfo.name); sb.append("\nType: " + packageInfo.type); sb.append("\nVisibility: " + packageInfo.visibility); if (packageInfo.state == PackageState.REMOTE && packageInfo.type != PackageType.STUDIO && packageInfo.visibility != PackageVisibility.PUBLIC && !LogicalInstanceIdentifier.isRegistered()) { sb.append(" (registration required)"); } sb.append("\nTarget platforms: " + ArrayUtils.toString(packageInfo.targetPlatforms)); appendIfNotEmpty(sb, "\nVendor: ", packageInfo.vendor); sb.append("\nSupports hot-reload: " + packageInfo.supportsHotReload); sb.append("\nSupported: " + packageInfo.supported); sb.append("\nProduction state: " + packageInfo.productionState); sb.append("\nValidation state: " + packageInfo.validationState); appendIfNotEmpty(sb, "\nProvides: ", packageInfo.provides); appendIfNotEmpty(sb, "\nDepends: ", packageInfo.dependencies); appendIfNotEmpty(sb, "\nConflicts: ", packageInfo.conflicts); appendIfNotEmpty(sb, "\nTitle: ", packageInfo.title); appendIfNotEmpty(sb, "\nDescription: ", packageInfo.description); appendIfNotEmpty(sb, "\nHomepage: ", packageInfo.homePage); appendIfNotEmpty(sb, "\nLicense: ", packageInfo.licenseType); appendIfNotEmpty(sb, "\nLicense URL: ", packageInfo.licenseUrl); sb.append("\n****************************************"); } catch (PackageException e) { cmdOk = false; cmdInfo.exitCode = 1; cmdInfo.newMessage(e); } } log.info(sb.toString()); return cmdOk; } private void appendIfNotEmpty(StringBuilder sb, String label, Object[] array) { if (ArrayUtils.isNotEmpty(array)) { sb.append(label + ArrayUtils.toString(array)); } } private void appendIfNotEmpty(StringBuilder sb, String label, String value) { if (StringUtils.isNotEmpty(value)) { sb.append(label + value); } } /** * Looks for a package. First look if it's a local ZIP file, second if it's a local package and finally if it's a * remote package. * * @param pkg A ZIP filename or file path, or package ID or a package name. * @return The first package found matching the given string. * @throws PackageException If no package is found or if an issue occurred while searching. * @see PackageDefinition * @see LocalPackage * @see DownloadablePackage */ protected Package findPackage(String pkg) throws PackageException { // Is it a local ZIP file? File localPackageFile = getLocalPackageFile(pkg); if (localPackageFile != null) { return service.loadPackageFromZip(localPackageFile); } // Is it a local package ID or name? LocalPackage localPackage = getLocalPackage(pkg); if (localPackage != null) { return localPackage; } // Is it a remote package ID or name? String pkgId = getRemotePackageId(pkg); if (pkgId != null) { return getPackageManager().findPackageById(pkgId); } throw new PackageException("Could not find a remote or local (relative to " + "current directory or to NUXEO_HOME) " + "package with name or ID " + pkg); } /** * @since 5.9.1 */ public void setAllowSNAPSHOT(boolean allow) { CUDFHelper.defaultAllowSNAPSHOT = allow; allowSNAPSHOT = allow; } }