From e3f4cc9383ee8b3a92e6777637391fb8c7bc7c42 Mon Sep 17 00:00:00 2001 From: Jean-Francois Denise Date: Thu, 21 Feb 2019 13:59:39 +0100 Subject: [PATCH 1/3] Fix for GAL-258, Ability to list optional packages --- .../jboss/galleon/cli/PmSessionCommand.java | 4 +- .../org/jboss/galleon/cli/cmd/Headers.java | 5 +- .../cli/cmd/featurepack/GetInfoCommand.java | 35 ++++- .../cli/cmd/state/InfoTypeCompleter.java | 5 +- .../galleon/cli/cmd/state/StateInfoUtil.java | 127 +++++++++++++++++- .../galleon/cli/model/FeatureContainer.java | 81 ++++++++++- .../galleon/cli/model/FeatureContainers.java | 106 ++++++++++++++- .../jboss/galleon/runtime/PackageRuntime.java | 22 ++- docs/guide/tool/state.adoc | 23 +++- .../galleon/cli/AdvancedLayersTestCase.java | 61 ++++++++- 10 files changed, 450 insertions(+), 19 deletions(-) diff --git a/cli/src/main/java/org/jboss/galleon/cli/PmSessionCommand.java b/cli/src/main/java/org/jboss/galleon/cli/PmSessionCommand.java index 429c38588..9d78a70d0 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/PmSessionCommand.java +++ b/cli/src/main/java/org/jboss/galleon/cli/PmSessionCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -47,7 +47,7 @@ public static void handleException(PmCommandInvocation session, Throwable t) thr if (session.getPmSession().isExceptionRethrown()) { throw new CommandException(t); } - // t.printStackTrace(); + //t.printStackTrace(); printException(session.getPmSession(), t); } diff --git a/cli/src/main/java/org/jboss/galleon/cli/cmd/Headers.java b/cli/src/main/java/org/jboss/galleon/cli/cmd/Headers.java index a08b7f71e..e7c4115c8 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/cmd/Headers.java +++ b/cli/src/main/java/org/jboss/galleon/cli/cmd/Headers.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,10 +30,13 @@ public class Headers { public static final String DEFAULT_VALUE = "Default Value"; public static final String DEPENDENCY = "Dependency"; public static final String DEPENDENCIES = "Dependencies"; + public static final String FEATURE = "Feature"; public static final String LATEST_BUILD = "Latest Build"; public static final String LAYERS = "Layers"; public static final String NAME = "Name"; public static final String OPTION = "Option"; + public static final String PACKAGE = "Package"; + public static final String PASSIVE = "Passive"; public static final String PATCH = "Patch"; public static final String PATCHES = "Patches"; public static final String PATCH_FOR = "Patch For"; diff --git a/cli/src/main/java/org/jboss/galleon/cli/cmd/featurepack/GetInfoCommand.java b/cli/src/main/java/org/jboss/galleon/cli/cmd/featurepack/GetInfoCommand.java index 2703ebb16..914ef7bbd 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/cmd/featurepack/GetInfoCommand.java +++ b/cli/src/main/java/org/jboss/galleon/cli/cmd/featurepack/GetInfoCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,8 +35,11 @@ import org.jboss.galleon.cli.cmd.CliErrors; import static org.jboss.galleon.cli.cmd.state.InfoTypeCompleter.ALL; import static org.jboss.galleon.cli.cmd.state.InfoTypeCompleter.LAYERS; +import static org.jboss.galleon.cli.cmd.state.InfoTypeCompleter.OPTIONAL_PACKAGES; import org.jboss.galleon.cli.cmd.state.StateInfoUtil; import org.jboss.galleon.cli.model.ConfigInfo; +import org.jboss.galleon.cli.model.FeatureContainer; +import org.jboss.galleon.cli.model.FeatureContainers; import static org.jboss.galleon.cli.path.FeatureContainerPathConsumer.CONFIGS; import static org.jboss.galleon.cli.path.FeatureContainerPathConsumer.DEPENDENCIES; import static org.jboss.galleon.cli.path.FeatureContainerPathConsumer.OPTIONS; @@ -65,7 +68,7 @@ public class InfoTypeCompleter extends AbstractCompleter { @Override protected List getItems(PmCompleterInvocation completerInvocation) { // No patch for un-customized FP. - return Arrays.asList(ALL, CONFIGS, DEPENDENCIES, LAYERS, OPTIONS); + return Arrays.asList(ALL, CONFIGS, DEPENDENCIES, LAYERS, OPTIONAL_PACKAGES, OPTIONS); } } @@ -147,6 +150,9 @@ protected void runCommand(PmCommandInvocation commandInvocation) throws CommandE if (displayLayers(commandInvocation, layout)) { commandInvocation.println(""); } + if (displayOptionalPackages(commandInvocation, layout)) { + commandInvocation.println(""); + } displayOptions(commandInvocation, layout); break; } @@ -174,6 +180,12 @@ protected void runCommand(PmCommandInvocation commandInvocation) throws CommandE } break; } + case OPTIONAL_PACKAGES: { + if (!displayOptionalPackages(commandInvocation, layout)) { + commandInvocation.println(StateInfoUtil.NO_OPTIONAL_PACKAGES); + } + break; + } default: { throw new CommandExecutionException(CliErrors.invalidInfoType()); } @@ -241,4 +253,23 @@ private boolean displayOptions(PmCommandInvocation commandInvocation, } return str != null; } + + private boolean displayOptionalPackages(PmCommandInvocation commandInvocation, + ProvisioningLayout pLayout) throws ProvisioningException, IOException { + Map> configs = new HashMap<>(); + try (ProvisioningRuntime rt = ProvisioningRuntimeBuilder. + newInstance(commandInvocation.getPmSession().getMessageWriter(false)) + .initRtLayout(pLayout.transform(ProvisioningRuntimeBuilder.FP_RT_FACTORY)) + .setEncoding(ProvisioningManager.Builder.ENCODING) + .build()) { + FeatureContainer container = FeatureContainers. + fromProvisioningRuntime(commandInvocation.getPmSession(), rt); + String str = StateInfoUtil.buildOptionalPackages(commandInvocation.getPmSession(), + container, pLayout); + if (str != null) { + commandInvocation.print(str); + } + return str != null; + } + } } diff --git a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/InfoTypeCompleter.java b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/InfoTypeCompleter.java index fec93ef24..3d1a4f44c 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/InfoTypeCompleter.java +++ b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/InfoTypeCompleter.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,11 +35,12 @@ public class InfoTypeCompleter extends AbstractCompleter { public static final String ALL = "all"; public static final String PATCHES = "patches"; public static final String LAYERS = "layers"; + public static final String OPTIONAL_PACKAGES = "optional-packages"; public static final String UNIVERSES = "universes"; @Override protected List getItems(PmCompleterInvocation completerInvocation) { - return Arrays.asList(ALL, CONFIGS, DEPENDENCIES, OPTIONS, LAYERS, PATCHES, UNIVERSES); + return Arrays.asList(ALL, CONFIGS, DEPENDENCIES, OPTIONAL_PACKAGES, OPTIONS, LAYERS, PATCHES, UNIVERSES); } } diff --git a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/StateInfoUtil.java b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/StateInfoUtil.java index 408fd0fbf..0f9918508 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/StateInfoUtil.java +++ b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/StateInfoUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,6 +32,7 @@ import org.jboss.galleon.ProvisioningOption; import org.jboss.galleon.cli.CommandExecutionException; import org.jboss.galleon.cli.PmCommandInvocation; +import org.jboss.galleon.cli.PmSession; import org.jboss.galleon.cli.cmd.CliErrors; import org.jboss.galleon.cli.cmd.Headers; import org.jboss.galleon.cli.cmd.Table; @@ -39,6 +40,7 @@ import org.jboss.galleon.cli.cmd.maingrp.LayersConfigBuilder; import static org.jboss.galleon.cli.cmd.state.InfoTypeCompleter.ALL; import static org.jboss.galleon.cli.cmd.state.InfoTypeCompleter.LAYERS; +import static org.jboss.galleon.cli.cmd.state.InfoTypeCompleter.OPTIONAL_PACKAGES; import static org.jboss.galleon.cli.cmd.state.InfoTypeCompleter.PATCHES; import static org.jboss.galleon.cli.cmd.state.InfoTypeCompleter.UNIVERSES; import org.jboss.galleon.cli.model.ConfigInfo; @@ -70,6 +72,7 @@ import org.jboss.galleon.spec.FeatureReferenceSpec; import org.jboss.galleon.universe.FeaturePackLocation; import org.jboss.galleon.universe.FeaturePackLocation.FPID; +import org.jboss.galleon.universe.FeaturePackLocation.ProducerSpec; import org.jboss.galleon.universe.UniverseSpec; /** @@ -83,6 +86,7 @@ public class StateInfoUtil { public static final String NO_CONFIGURATIONS = "No configurations."; public static final String NO_DEPENDENCIES = "No dependencies."; public static final String NO_LAYERS = "No layers."; + public static final String NO_OPTIONAL_PACKAGES = "No optional packages."; public static final String NO_OPTIONS = "No options."; public static final String NO_PATCHES = "No patches."; public static final String NO_UNIVERSES = "No custom universes."; @@ -357,6 +361,110 @@ public static String buildConfigs(Map> configs, return null; } + public static String buildOptionalPackages(PmSession session, FeatureContainer container, + ProvisioningLayout pLayout) throws ProvisioningException, IOException { + String optionValue + = container.getProvisioningConfig().getOption(ProvisioningOption.OPTIONAL_PACKAGES.getName()); + if (optionValue == null) { + optionValue = Constants.ALL; + } + Table.Tree t = null; + boolean passivePresent = !container.getPassivePackages().isEmpty() + || !container.getOrphanPassivePackages().isEmpty(); + Set optionalProducers = container.getOptionalPackagesProducers(); + if (!optionalProducers.isEmpty()) { + if (passivePresent) { + t = new Table.Tree(Headers.PRODUCT, Headers.FEATURE, Headers.PACKAGE, Headers.PASSIVE); + } else { + t = new Table.Tree(Headers.PRODUCT, Headers.FEATURE, Headers.PACKAGE); + } + for (String producer : optionalProducers) { + String displayProducer = producer; + try { + ProducerSpec pSpec = FeaturePackLocation.fromString(producer).getProducer(); + if (session.getUniverse().getBuiltinUniverseSpec().equals(pSpec.getUniverse())) { + displayProducer = pSpec.getName(); + } + } catch (Exception ex) { + // Not a producerSpec, keep original one. + } + Table.Node producerNode = new Table.Node(displayProducer); + t.add(producerNode); + Map> optionalPkgs = container.getOptionalPackages().get(producer); + if (optionalPkgs != null && !optionalPkgs.isEmpty()) { + for (Entry> entry : optionalPkgs.entrySet()) { + Table.Node feat = new Table.Node(entry.getKey()); + producerNode.addNext(feat); + for (String p : entry.getValue()) { + Table.Node pkg = new Table.Node(p); + feat.addNext(pkg); + if (passivePresent) { + pkg.addNext(new Table.Node("")); + } + } + Map> passivePkgs = container.getPassivePackages().get(producer); + if (passivePkgs != null) { + Set passives = passivePkgs.get(entry.getKey()); + if (passives != null) { + for (String p : passives) { + Table.Node pkg = new Table.Node(p); + feat.addNext(pkg); + pkg.addNext(new Table.Node("true")); + } + } + } + } + } else { + Map> passivePkgs = container.getPassivePackages().get(producer); + if (passivePkgs != null && !passivePkgs.isEmpty()) { + for (Entry> entry : passivePkgs.entrySet()) { + Table.Node feat = new Table.Node(entry.getKey()); + producerNode.addNext(feat); + for (String p : entry.getValue()) { + Table.Node pkg = new Table.Node(p); + feat.addNext(pkg); + pkg.addNext(new Table.Node("true")); + } + } + } + } + Set orphanOptionals = container.getOrphanOptionalPackages().get(producer); + Set orphanPassives = container.getOrphanPassivePackages().get(producer); + if (orphanOptionals != null + || orphanPassives != null) { + Table.Node feat = new Table.Node("{no-feature}"); + producerNode.addNext(feat); + + if (orphanOptionals != null) { + for (String p : orphanOptionals) { + Table.Node pkg = new Table.Node(p); + feat.addNext(pkg); + if (passivePresent) { + pkg.addNext(new Table.Node("")); + } + } + } + if (orphanPassives != null) { + for (String p : orphanPassives) { + Table.Node pkg = new Table.Node(p); + feat.addNext(pkg); + pkg.addNext(new Table.Node("true")); + } + } + } + } + } + StringBuilder builder = new StringBuilder(); + builder.append("Optional packages (Provisioning option: " + optionValue + ")" + + Config.getLineSeparator()); + if (t == null) { + builder.append(NO_OPTIONAL_PACKAGES); + } else { + builder.append(t.build()); + } + return builder.toString(); + } + public static String buildLayers(ProvisioningLayout pLayout) throws ProvisioningException, IOException { Map>> layersMap = LayersConfigBuilder.getAllLayers(pLayout); if (!layersMap.isEmpty()) { @@ -562,6 +670,9 @@ public static void displayInfo(PmCommandInvocation invoc, Path installation, if (displayLayers(invoc, layout)) { invoc.println(""); } + if (displayOptionalPackages(invoc, container, layout)) { + invoc.println(""); + } if (displayOptions(invoc, layout)) { invoc.println(""); } @@ -623,6 +734,13 @@ public static void displayInfo(PmCommandInvocation invoc, Path installation, } break; } + case OPTIONAL_PACKAGES: { + FeatureContainer container = supplier.apply(layout); + String packages = buildOptionalPackages(invoc.getPmSession(), + container, layout); + invoc.print(packages); + break; + } default: { throw new CommandExecutionException(CliErrors.invalidInfoType()); } @@ -648,6 +766,13 @@ private static String buildConfigs(PmCommandInvocation invoc, FeatureContainer c return buildConfigs(container.getFinalConfigs(), pLayout); } + private static boolean displayOptionalPackages(PmCommandInvocation invoc, FeatureContainer container, + ProvisioningLayout pLayout) throws ProvisioningException, IOException { + String str = buildOptionalPackages(invoc.getPmSession(), container, pLayout); + invoc.print(str); + return true; + } + private static void displayFeaturePacks(PmCommandInvocation invoc, Path installation, ProvisioningConfig config) { String str = buildFeaturePacks(invoc, installation, config.getFeaturePackDeps()); diff --git a/cli/src/main/java/org/jboss/galleon/cli/model/FeatureContainer.java b/cli/src/main/java/org/jboss/galleon/cli/model/FeatureContainer.java index c9c8a74e2..e220cd79f 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/model/FeatureContainer.java +++ b/cli/src/main/java/org/jboss/galleon/cli/model/FeatureContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,6 +23,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; import org.jboss.galleon.config.ConfigId; import org.jboss.galleon.config.ProvisioningConfig; @@ -50,6 +52,13 @@ public abstract class FeatureContainer { private final ProvisioningConfig config; private final Set layers = new HashSet<>(); + private final Set optionalPackagesProducers = new TreeSet<>(); + private final Map>> optionalPackages = new TreeMap<>(); + private final Map>> passivePackages = new TreeMap<>(); + + private final Map> orphanOptionalPackages = new TreeMap<>(); + private final Map> orphanPassivePackages = new TreeMap<>(); + protected FeatureContainer(String name, FPID fpid, ProvisioningConfig config) { this.name = name; this.fpid = fpid; @@ -143,4 +152,74 @@ void addLayers(Set layers) { public Set getLayers() { return layers; } + + void addOptionalPackage(String producer, String spec, String pkg) { + Map> map = optionalPackages.get(producer); + if (map == null) { + optionalPackagesProducers.add(producer); + map = new TreeMap<>(); + optionalPackages.put(producer, map); + } + Set set = map.get(spec); + if (set == null) { + set = new TreeSet<>(); + map.put(spec, set); + } + set.add(pkg); + } + + void addPassivePackage(String producer, String spec, String pkg) { + Map> map = passivePackages.get(producer); + if (map == null) { + optionalPackagesProducers.add(producer); + map = new TreeMap<>(); + passivePackages.put(producer, map); + } + Set set = map.get(spec); + if (set == null) { + set = new TreeSet<>(); + map.put(spec, set); + } + set.add(pkg); + } + + void addOrphanOptionalPackage(String producer, String pkg) { + Set set = orphanOptionalPackages.get(producer); + if (set == null) { + optionalPackagesProducers.add(producer); + set = new TreeSet<>(); + orphanOptionalPackages.put(producer, set); + } + set.add(pkg); + } + + void addOrphanPassivePackage(String producer, String pkg) { + Set set = orphanPassivePackages.get(producer); + if (set == null) { + optionalPackagesProducers.add(producer); + set = new TreeSet<>(); + orphanPassivePackages.put(producer, set); + } + set.add(pkg); + } + + public Set getOptionalPackagesProducers() { + return Collections.unmodifiableSet(optionalPackagesProducers); + } + + public Map>> getOptionalPackages() { + return Collections.unmodifiableMap(optionalPackages); + } + + public Map>> getPassivePackages() { + return Collections.unmodifiableMap(passivePackages); + } + + public Map> getOrphanOptionalPackages() { + return Collections.unmodifiableMap(orphanOptionalPackages); + } + + public Map> getOrphanPassivePackages() { + return Collections.unmodifiableMap(orphanPassivePackages); + } } diff --git a/cli/src/main/java/org/jboss/galleon/cli/model/FeatureContainers.java b/cli/src/main/java/org/jboss/galleon/cli/model/FeatureContainers.java index 46b2325e4..a36acf4d1 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/model/FeatureContainers.java +++ b/cli/src/main/java/org/jboss/galleon/cli/model/FeatureContainers.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,9 +24,11 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import org.jboss.galleon.Constants; import org.jboss.galleon.ProvisioningException; import org.jboss.galleon.ProvisioningManager; +import org.jboss.galleon.ProvisioningOption; import org.jboss.galleon.cli.PmSession; import org.jboss.galleon.config.FeaturePackConfig; import org.jboss.galleon.config.ProvisioningConfig; @@ -38,6 +40,8 @@ import org.jboss.galleon.runtime.ProvisioningRuntime; import org.jboss.galleon.runtime.ProvisioningRuntimeBuilder; import org.jboss.galleon.runtime.ResolvedSpecId; +import org.jboss.galleon.spec.FeatureSpec; +import org.jboss.galleon.spec.PackageDependencySpec; import org.jboss.galleon.state.ProvisionedConfig; import org.jboss.galleon.state.ProvisionedFeature; import org.jboss.galleon.universe.FeaturePackLocation; @@ -93,10 +97,52 @@ public void visitPlugin(CliPlugin plugin) throws ProvisioningException { CliPlugin plugin = cliPlugins.isEmpty() ? null : cliPlugins.get(0); PackageGroupsBuilder pkgBuilder = new PackageGroupsBuilder(); FeatureSpecsBuilder specsBuilder = new FeatureSpecsBuilder(); + String optionalOption = runtime.getProvisioningConfig(). + getOption(ProvisioningOption.OPTIONAL_PACKAGES.getName()); + if (optionalOption == null) { + optionalOption = Constants.ALL; + } + boolean includeOptional = false; + boolean includePassive = false; + boolean checkPassive = false; + if (optionalOption.equals(Constants.ALL)) { + includePassive = true; + includeOptional = true; + } else { + if (optionalOption.equals(Constants.NONE)) { + // No optional included. + includeOptional = false; + includePassive = false; + } else { + if (optionalOption.equals(Constants.PASSIVE)) { + // Include passives that have dependencies present. + includeOptional = false; + includePassive = true; + checkPassive = true; + } else { + if (optionalOption.equals(Constants.PASSIVE_PLUS)) { + // Include passives that have dependencies present and optionals + includeOptional = true; + includePassive = true; + checkPassive = true; + } else { + throw new ProvisioningException("Not recognized value for " + Constants.OPTIONAL_PACKAGES); + } + } + } + } + boolean includeOptionalF = includeOptional; + boolean includePassiveF = includePassive; + Map> allPackages = new HashMap<>(); for (FeaturePackRuntime rt : runtime.getFeaturePacks()) { fp.addLayers(rt.loadLayers()); pkgBuilder.resetRoots(); + allPackages.put(rt.getFPID().getProducer().toString(), new HashMap<>()); for (PackageRuntime pkg : rt.getPackages()) { + if (checkPassive && pkg.isPassive() && !pkg.isPassiveIncluded()) { // exclude passives that don't match. + continue; + } + allPackages.get(rt.getFPID().getProducer().toString()).put(pkg.getName(), pkg); pkgBuilder.buildGroups(new PackageInfo(pkg, Identity. fromChannel(rt.getFPID().getProducer(), pkg.getName()), plugin), new PackageGroupsBuilder.PackageInfoBuilder() { @Override @@ -176,6 +222,35 @@ public PackageInfo build(Identity identity, PackageInfo parent) { c.handle(new ProvisionedConfigHandler() { @Override public void nextFeature(ProvisionedFeature feature) throws ProvisioningException { + FeaturePackRuntime rt = runtime.getFeaturePack(feature.getSpecId().getProducer()); + FeatureSpec featureSpec = rt.getFeatureSpec(feature.getSpecId().getName()); + ProducerSpec producer = feature.getSpecId().getProducer(); + for (PackageDependencySpec spec : featureSpec.getLocalPackageDeps()) { + PackageRuntime pkg = allPackages.get(producer.toString()).get(spec.getName()); + if(pkg != null) { + if (includePassiveF && pkg.isPassive()) { + fp.addPassivePackage(producer.toString(), feature.getSpecId().getName(), spec.getName()); + } else { + if (includeOptionalF && pkg.isOptional()) { + fp.addOptionalPackage(producer.toString(), feature.getSpecId().getName(), spec.getName()); + } + } + } + } + if (featureSpec.hasExternalPackageDeps()) { + for (String origin : featureSpec.getPackageOrigins()) { + for (PackageDependencySpec spec : featureSpec.getExternalPackageDeps(origin)) { + PackageRuntime pkg = allPackages.get(origin).get(spec.getName()); + if (pkg != null) { + if (includePassiveF && pkg.isPassive()) { + fp.addPassivePackage(origin, feature.getSpecId().getName(), spec.getName()); + } else if (includeOptionalF && pkg.isOptional()) { + fp.addOptionalPackage(origin, feature.getSpecId().getName(), spec.getName()); + } + } + } + } + } Set set = actualSet.get(feature.getSpecId().getProducer()); if (set == null) { set = new HashSet<>(); @@ -203,6 +278,35 @@ public void nextFeature(ProvisionedFeature feature) throws ProvisioningException }); config.setFeatureGroupRoot(grpBuilder.getRoot()); } + // Handle packages that are not directly referenced from a feature. + for (String producer : allPackages.keySet()) { + Set allOptionals = new HashSet<>(); + Set allPassives = new HashSet<>(); + Map> optionals = fp.getOptionalPackages().get(producer); + if (optionals != null) { + for (Set vals : optionals.values()) { + allOptionals.addAll(vals); + } + } + Map> passives = fp.getPassivePackages().get(producer); + if (passives != null) { + for (Set vals : passives.values()) { + allPassives.addAll(vals); + } + } + Map packages = allPackages.get(producer); + for (Entry entry : packages.entrySet()) { + String name = entry.getKey(); + PackageRuntime pkg = entry.getValue(); + if (!allOptionals.contains(name) && !allPassives.contains(name)) { + if (includePassiveF && pkg.isPassive()) { + fp.addOrphanPassivePackage(producer, name); + } else if (includeOptionalF && pkg.isOptional()) { + fp.addOrphanOptionalPackage(producer, name); + } + } + } + } if (!allSpecs) { // Build the set of FeatureSpecInfo, side effect is to connect // packages and feature-specs. diff --git a/core/src/main/java/org/jboss/galleon/runtime/PackageRuntime.java b/core/src/main/java/org/jboss/galleon/runtime/PackageRuntime.java index 634b25c74..0c56c498a 100644 --- a/core/src/main/java/org/jboss/galleon/runtime/PackageRuntime.java +++ b/core/src/main/java/org/jboss/galleon/runtime/PackageRuntime.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -164,10 +164,30 @@ static Builder builder(FeaturePackRuntimeBuilder fp, PackageSpec spec, Path dir, private final PackageSpec spec; private final Path layoutDir; + private final int status; + private final boolean isPassiveIncluded; + private final boolean passive; + private PackageRuntime(Builder builder, FeaturePackRuntime fp) { this.fp = fp; this.spec = builder.spec; this.layoutDir = builder.dir; + passive = builder.type == PackageDependencySpec.PASSIVE; + isPassiveIncluded = passive + && builder.isPassiveWithSatisfiedDeps(); + status = builder.status; + } + + public boolean isPassive() { + return passive; + } + + public boolean isOptional() { + return (status & INCLUDED) == 0 && ((status & PARENT_INCLUDED) > 0 || (status & ROOT) > 0); + } + + public boolean isPassiveIncluded() { + return isPassiveIncluded; } public FeaturePackRuntime getFeaturePackRuntime() { diff --git a/docs/guide/tool/state.adoc b/docs/guide/tool/state.adoc index 1ce781f23..469d7edcb 100644 --- a/docs/guide/tool/state.adoc +++ b/docs/guide/tool/state.adoc @@ -197,18 +197,37 @@ into the provisioning configuration, this operation has no side effect. ### Observing an installation -_[my-dir]$ installation get-info [--dir=installation] --type=[all|configs|dependencies|layers|patches]_ +_[my-dir]$ installation get-info [--dir=installation] --type=[all|configs|dependencies|layers|optional-packages|options|patches|universes]_ Display the set of installed feature-packs FPID. In addition can display configurations, dependencies layers and patches. + NB: If some patches are applied, the applied patches information is displayed. ### Observing a feature-pack -_[my-dir]$ feature-pack get-info <[FPL|FPID] | [--file=]> --type=[all|configs|dependencies|layers|options]_ +_[my-dir]$ feature-pack get-info <[FPL|FPID] | [--file=]> --type=[all|configs|dependencies|layers|optional-packages|options]_ Display the FPID of a feature-pack. In addition can display dependencies, configurations, layers and options usable when installing/provisioning/upgrading. +### Optional packages + +When getting information on an installation or a feature-pack, using _--type=optional-packages_ option, you can retrieve the list of +optional packages. At installation time the set of optional packages installed is controlled by the option _--optional-packages=_. + +There are 2 kind of optional packages. _passive_ ones, that are installed only if all their dependencies are installed and _optional_ +ones that have no requirements on their dependencies. Generally passive optional packages shouldn't be excluded from an installation. They +are needed for the package to properly operate when the other packages on which it depends are provisioned. Optional packages that are not passive +can be excluded if not needed (according to the execution context). + +When installing a complete feature-pack or installing a default configuration, the default value for _--optional-packages_ is +_all_ (all optional and passive packages are installed). + +When installing layers with the install commands the default value for _--optional-packages_ is +_passive+_ (all optional and passive packages that have all their dependencies installed are installed). + +You can override these default values by using the _--optional-packages_ option. +Use _none_ value to not install any optional nor passive packages, _passive_ value to only install passive packages that have all their dependencies installed. + ### Managing the local cache of feature-packs When a feature-pack is internally resolved (at install time, to expose information, diff --git a/testsuite/src/test/java/org/jboss/galleon/cli/AdvancedLayersTestCase.java b/testsuite/src/test/java/org/jboss/galleon/cli/AdvancedLayersTestCase.java index 187ec8632..facae7e5e 100644 --- a/testsuite/src/test/java/org/jboss/galleon/cli/AdvancedLayersTestCase.java +++ b/testsuite/src/test/java/org/jboss/galleon/cli/AdvancedLayersTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,9 +25,11 @@ import static org.jboss.galleon.cli.CliTestUtils.PRODUCER2; import static org.jboss.galleon.cli.CliTestUtils.PRODUCER3; import static org.jboss.galleon.cli.CliTestUtils.UNIVERSE_NAME; +import org.jboss.galleon.config.ConfigModel; import org.jboss.galleon.config.FeatureConfig; import org.jboss.galleon.creator.FeaturePackCreator; import org.jboss.galleon.spec.ConfigLayerSpec; +import org.jboss.galleon.spec.FeatureParameterSpec; import org.jboss.galleon.spec.FeatureSpec; import org.jboss.galleon.spec.PackageDependencySpec; import org.jboss.galleon.state.ProvisionedState; @@ -59,7 +61,7 @@ public static void setup() throws Exception { @AfterClass public static void tearDown() { - cli.close(); + //cli.close(); } @Test @@ -80,6 +82,12 @@ public void test() throws Exception { assertTrue(pkgs.toString(), pkgs.contains("p1-ref-from-optional2")); assertFalse(pkgs.toString(), pkgs.contains("p1-passive")); assertTrue(pkgs.toString(), pkgs.contains("p2-passive")); + + cli.execute("get-info --dir=" + path + " --type=optional-packages"); + assertTrue(cli.getOutput(), cli.getOutput().contains("p1-optional")); + assertFalse(cli.getOutput().contains("p1-passive")); + assertTrue(cli.getOutput().contains("p2-passive")); + assertFalse(cli.getOutput().contains("p1-required")); } { Path path = cli.newDir("prod2", false); @@ -98,6 +106,44 @@ public void test() throws Exception { assertTrue(pkgs.toString(), pkgs.contains("p2-required")); assertTrue(pkgs.toString(), pkgs.contains("p2-optional")); } + { + Path path = cli.newDir("prod3", false); + cli.execute("install " + prod1 + " --dir=" + path + + " --layers=layerA-" + PRODUCER1 + " --optional-packages=none"); + + cli.execute("get-info --dir=" + path + " --type=optional-packages"); + assertTrue(cli.getOutput().contains("No optional packages.")); + assertFalse(cli.getOutput().contains("p1-passive")); + assertFalse(cli.getOutput().contains("p2-passive")); + assertFalse(cli.getOutput().contains("p1-optional")); + } + { + Path path = cli.newDir("prod4", false); + cli.execute("install " + prod1 + " --dir=" + path + + " --layers=layerA-" + PRODUCER1 + " --optional-packages=passive"); + + cli.execute("get-info --dir=" + path + " --type=optional-packages"); + assertFalse(cli.getOutput().contains("p1-passive")); + assertTrue(cli.getOutput().contains("p2-passive")); + assertFalse(cli.getOutput().contains("p1-optional")); + } + { + Path path = cli.newDir("prod4", false); + cli.execute("install " + prod1 + " --dir=" + path + + " --layers=layerA-" + PRODUCER1 + " --optional-packages=all"); + + cli.execute("get-info --dir=" + path + " --type=optional-packages"); + assertTrue(cli.getOutput().contains("p1-passive")); + assertTrue(cli.getOutput().contains("p2-passive")); + assertTrue(cli.getOutput().contains("p1-optional")); + assertFalse(cli.getOutput().contains("p2-optional")); + + cli.execute("feature-pack get-info " + prod1 + " --type=optional-packages"); + assertTrue(cli.getOutput().contains("p1-passive")); + assertTrue(cli.getOutput().contains("p2-passive")); + assertTrue(cli.getOutput().contains("p1-optional")); + assertTrue(cli.getOutput().contains("p2-optional")); + } } @Test @@ -157,21 +203,24 @@ private static void buildFP(CliWrapper cli, UniverseSpec universeSpec, addPackageDep("p1-optional", true). addPackageDep(PackageDependencySpec.newInstance("p1-passive", PackageDependencySpec.PASSIVE)). addPackageDep(PackageDependencySpec.newInstance("p2-passive", PackageDependencySpec.PASSIVE)). - setName("feat1").build()) + setName("feat1").addParam(FeatureParameterSpec.createId("p1")).build()) .addFeatureSpec(FeatureSpec.builder().addPackageDep("p2-required"). addPackageDep("p2-optional", true). - setName("feat2").build()) + setName("feat2").addParam(FeatureParameterSpec.createId("p2")).build()) .addConfigLayer(ConfigLayerSpec.builder() .setModel("testmodel").setName("base-" + producer) .build()) .addConfigLayer(ConfigLayerSpec.builder() .setModel("testmodel").setName("layerA-" + producer) - .addLayerDep("base-" + producer).addConfigItem(FeatureConfig.newConfig("feat1")) + .addLayerDep("base-" + producer).addFeature(FeatureConfig.newConfig("feat1").setParam("p1", "1")) .build()) .addConfigLayer(ConfigLayerSpec.builder() .setModel("testmodel").setName("layerB-" + producer) - .addLayerDep("base-" + producer).addConfigItem(FeatureConfig.newConfig("feat2")) + .addLayerDep("base-" + producer).addFeature(FeatureConfig.newConfig("feat2").setParam("p2", "1")) .build()) + .addConfig(ConfigModel.builder("testmodel", "foo.xml"). + includeLayer("layerA-" + producer). + includeLayer("layerB-" + producer).build(), true) .newPackage("p1-required", false).addDependency("p1-ref-from-required") .writeContent("fp1/p1-required.txt", "fp1 p1").getFeaturePack(). newPackage("p1-ref-from-required", false) From 6d910b11037cc44b244a6b29c4a30b9737398563 Mon Sep 17 00:00:00 2001 From: Jean-Francois Denise Date: Thu, 21 Feb 2019 19:15:57 +0100 Subject: [PATCH 2/3] Fix for GAL-260, Excluded optional package shouldn't be resolved --- .../java/org/jboss/galleon/cli/model/FeatureContainers.java | 4 ++-- .../org/jboss/galleon/cli/model/PackageGroupsBuilder.java | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cli/src/main/java/org/jboss/galleon/cli/model/FeatureContainers.java b/cli/src/main/java/org/jboss/galleon/cli/model/FeatureContainers.java index a36acf4d1..39e1754b3 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/model/FeatureContainers.java +++ b/cli/src/main/java/org/jboss/galleon/cli/model/FeatureContainers.java @@ -192,8 +192,8 @@ public PackageInfo build(Identity identity, PackageInfo parent) { } if (p == null) { - throw new RuntimeException("Package " + pkg.getName() - + ", unknown dependency " + identity + " local is " + currentRuntime.getFPID()); + // Optional package that has been excluded + return null; } return new PackageInfo(p, resolvedIdentity, plugin); diff --git a/cli/src/main/java/org/jboss/galleon/cli/model/PackageGroupsBuilder.java b/cli/src/main/java/org/jboss/galleon/cli/model/PackageGroupsBuilder.java index 60b0ea938..0806f81ab 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/model/PackageGroupsBuilder.java +++ b/cli/src/main/java/org/jboss/galleon/cli/model/PackageGroupsBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -60,6 +60,10 @@ void buildGroups(PackageInfo pkg, PackageInfoBuilder builder) { } private void buildGroups(Group grp, PackageInfo pkg, PackageInfoBuilder builder) { + if (pkg == null) { + // Excluded optional package referenced from other package. + return; + } Group gp = allPackagesGroups.get(pkg.getIdentity()); if (gp == null) { gp = Group.fromIdentity(pkg.getIdentity()); From 069b51d140a092db7483a245b6f7c3ccac9361cc Mon Sep 17 00:00:00 2001 From: Jean-Francois Denise Date: Fri, 22 Feb 2019 17:08:10 +0100 Subject: [PATCH 3/3] Fix for GAL-259, CLI, Including, excluding packages and transitive dependency issues --- .../state/AbstractFPProvisionedCommand.java | 13 +- .../cmd/state/pkg/AbstractPackageCommand.java | 22 +- .../AbstractProvisionedPackageCommand.java | 9 +- .../cli/cmd/state/pkg/PackagesUtil.java | 10 + .../ProvisionedPackageCommandActivator.java | 7 +- .../state/pkg/StateExcludePackageCommand.java | 12 +- .../state/pkg/StateIncludePackageCommand.java | 12 +- .../model/state/FeaturePackProvisioning.java | 91 +++++++- .../jboss/galleon/cli/model/state/State.java | 11 + .../config/FeaturePackDepsConfigBuilder.java | 6 +- .../org/jboss/galleon/cli/CliTestUtils.java | 1 + .../org/jboss/galleon/cli/StateTestCase.java | 218 +++++++++++++++++- 12 files changed, 386 insertions(+), 26 deletions(-) diff --git a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/AbstractFPProvisionedCommand.java b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/AbstractFPProvisionedCommand.java index 59ec99aa5..fcaba95df 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/AbstractFPProvisionedCommand.java +++ b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/AbstractFPProvisionedCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -42,13 +42,18 @@ protected void runCommand(PmCommandInvocation invoc, State session) throws IOExc } public FeaturePackConfig getProvisionedFP(PmSession session) throws CommandExecutionException { - ProducerSpec channel = getProducer(session); - if (channel == null) { + ProducerSpec producer = getProducer(session); + if (producer == null) { return null; } ProvisioningConfig config = session.getState().getConfig(); for (FeaturePackConfig dep : config.getFeaturePackDeps()) { - if (dep.getLocation().getProducer().equals(channel)) { + if (dep.getLocation().getProducer().equals(producer)) { + return dep; + } + } + for (FeaturePackConfig dep : config.getTransitiveDeps()) { + if (dep.getLocation().getProducer().equals(producer)) { return dep; } } diff --git a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/AbstractPackageCommand.java b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/AbstractPackageCommand.java index 8ef6b1280..49d424252 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/AbstractPackageCommand.java +++ b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/AbstractPackageCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import org.aesh.command.option.Argument; import org.jboss.galleon.universe.FeaturePackLocation; import org.jboss.galleon.universe.FeaturePackLocation.ProducerSpec; @@ -137,11 +136,10 @@ public FeaturePackConfig getProvisionedFP(PmSession session) throws CommandExecu String orig = getPackage().substring(0, i); String name = getPackage().substring(i + 1); FeaturePackLocation.FPID fpid = null; - for (Entry entry : session.getContainer().getFullDependencies().entrySet()) { - FeatureContainer container = entry.getValue(); + FeatureContainer container = session.getContainer().getFullDependencies().get(orig); + if (container != null) { if (container.getAllPackages().containsKey(Identity.fromString(orig, name))) { fpid = container.getFPID(); - break; } } if (fpid == null) { @@ -153,9 +151,17 @@ public FeaturePackConfig getProvisionedFP(PmSession session) throws CommandExecu break; } } - } - if (config == null) { - throw new CommandExecutionException("No feature pack found for " + getPackage()); + if (config == null) { + // reset buildID + FeaturePackLocation noBuildLocation = new FeaturePackLocation(fpid.getUniverse(), fpid.getProducer().getName(), + null, null, null); + for (FeaturePackConfig c : session.getState().getConfig().getTransitiveDeps()) { + if (c.getLocation().equals(c.getLocation().hasBuild() ? fpid.getLocation() : noBuildLocation)) { + config = c; + break; + } + } + } } return config; } diff --git a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/AbstractProvisionedPackageCommand.java b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/AbstractProvisionedPackageCommand.java index 67de4e797..d08d213b3 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/AbstractProvisionedPackageCommand.java +++ b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/AbstractProvisionedPackageCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -59,6 +59,13 @@ protected List getItems(PmCompleterInvocation completerInvocation) { } } } + for (FeaturePackConfig fc : completerInvocation.getPmSession().getState().getConfig().getTransitiveDeps()) { + for (String pkg : cmd.getTargetedPackages(fc)) { + if (!packages.contains(pkg)) { + packages.add(pkg); + } + } + } } else { for (String pkg : cmd.getTargetedPackages(fp)) { if (!packages.contains(pkg)) { diff --git a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/PackagesUtil.java b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/PackagesUtil.java index 358cba3b4..a194dae4d 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/PackagesUtil.java +++ b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/PackagesUtil.java @@ -59,6 +59,11 @@ public static Map getIncludedPackages(PmSession sessi packages.put(c, pkg); } } + for (FeaturePackConfig c : session.getState().getConfig().getTransitiveDeps()) { + if (c.getIncludedPackages().contains(pkg)) { + packages.put(c, pkg); + } + } } else { if (config.getIncludedPackages().contains(pkg)) { packages.put(config, pkg); @@ -78,6 +83,11 @@ public static Map getExcludedPackages(PmSession sessi packages.put(c, pkg); } } + for (FeaturePackConfig c : session.getState().getConfig().getTransitiveDeps()) { + if (c.getExcludedPackages().contains(pkg)) { + packages.put(c, pkg); + } + } } else { if (config.getExcludedPackages().contains(pkg)) { packages.put(config, pkg); diff --git a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/ProvisionedPackageCommandActivator.java b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/ProvisionedPackageCommandActivator.java index d6d919542..e173cae66 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/ProvisionedPackageCommandActivator.java +++ b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/ProvisionedPackageCommandActivator.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,6 +34,11 @@ public boolean isActivated(ParsedCommand command) { return true; } } + for (FeaturePackConfig cf : getSession().getState().getConfig().getTransitiveDeps()) { + if (!cmd.getTargetedPackages(cf).isEmpty()) { + return true; + } + } return false; } } diff --git a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/StateExcludePackageCommand.java b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/StateExcludePackageCommand.java index 08007fb4c..50ccb3563 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/StateExcludePackageCommand.java +++ b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/StateExcludePackageCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -38,7 +38,15 @@ public class StateExcludePackageCommand extends AbstractPackageCommand { @Override protected void runCommand(PmCommandInvocation invoc, State session, FeaturePackConfig config) throws IOException, ProvisioningException, CommandExecutionException { try { - session.excludePackage(invoc.getPmSession(), PackagesUtil.getPackage(invoc.getPmSession(), config.getLocation().getFPID(), getPackage()), config); + int i = getPackage().indexOf("/"); + String name = getPackage().substring(i + 1); + // If the config is null, it means that the package exists in a transitive dependency + // but no transitive dependency has been created yet. + if (config == null) { + session.excludePackageFromNewTransitive(invoc.getPmSession(), getProducer(invoc.getPmSession()), name); + } else { + session.excludePackage(invoc.getPmSession(), name, config); + } } catch (Exception ex) { throw new CommandExecutionException(invoc.getPmSession(), CliErrors.excludeFailed(), ex); } diff --git a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/StateIncludePackageCommand.java b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/StateIncludePackageCommand.java index ed841a857..61a14252f 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/StateIncludePackageCommand.java +++ b/cli/src/main/java/org/jboss/galleon/cli/cmd/state/pkg/StateIncludePackageCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -38,7 +38,15 @@ public class StateIncludePackageCommand extends AbstractPackageCommand { @Override protected void runCommand(PmCommandInvocation invoc, State session, FeaturePackConfig config) throws IOException, ProvisioningException, CommandExecutionException { try { - session.includePackage(invoc.getPmSession(), PackagesUtil.getPackage(invoc.getPmSession(), config.getLocation().getFPID(), getPackage()), config); + int i = getPackage().indexOf("/"); + String name = getPackage().substring(i + 1); + // If the config is null, it means that the package exists in a transitive dependency + // but no transitive dependency has been created yet. + if (config == null) { + session.includePackageInNewTransitive(invoc.getPmSession(), getProducer(invoc.getPmSession()), name); + } else { + session.includePackage(invoc.getPmSession(), name, config); + } } catch (Exception ex) { throw new CommandExecutionException(invoc.getPmSession(), CliErrors.includeFailed(), ex); } diff --git a/cli/src/main/java/org/jboss/galleon/cli/model/state/FeaturePackProvisioning.java b/cli/src/main/java/org/jboss/galleon/cli/model/state/FeaturePackProvisioning.java index ef3bd19a0..c252413dd 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/model/state/FeaturePackProvisioning.java +++ b/cli/src/main/java/org/jboss/galleon/cli/model/state/FeaturePackProvisioning.java @@ -17,7 +17,9 @@ package org.jboss.galleon.cli.model.state; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -28,6 +30,7 @@ import org.jboss.galleon.config.ConfigId; import org.jboss.galleon.config.FeaturePackConfig; import org.jboss.galleon.config.ProvisioningConfig; +import org.jboss.galleon.universe.FeaturePackLocation.ProducerSpec; /** * @@ -84,7 +87,7 @@ private abstract class AbstractAction implements State.Action { private final Map cf; private final Map indexes = new HashMap<>(); - + private final List transitives = new ArrayList<>(); AbstractAction(Map cf) { this.cf = cf; } @@ -98,14 +101,32 @@ public void doAction(ProvisioningConfig current, ProvisioningConfig.Builder buil boolean doit = doAction(fpBuilder, entry.getValue()); // this complexity is due to the fact that some fp could already have the configuration included/excluded/... if (doit) { - int index = builder.getFeaturePackDepIndex(entry.getKey().getLocation()); - indexes.put(entry.getKey().getLocation().getFPID(), index); - builder.removeFeaturePackDep(entry.getKey().getLocation()); - builder.addFeaturePackDep(index, fpBuilder.build()); + FeaturePackConfig cfg = fpBuilder.build(); + if (cfg.isTransitive()) { + builder.removeTransitiveDep(cfg.getLocation().getFPID()); + // Do not add back empty transitive + if (cfg.getLocation().getBuild() != null || !isEmptyConfig(cfg)) { + builder.addFeaturePackDep(cfg); + } + transitives.add(entry.getKey()); + } else { + int index = builder.getFeaturePackDepIndex(entry.getKey().getLocation()); + indexes.put(entry.getKey().getLocation().getFPID(), index); + builder.removeFeaturePackDep(entry.getKey().getLocation()); + builder.addFeaturePackDep(index, fpBuilder.build()); + } } } } + private boolean isEmptyConfig(FeaturePackConfig cfg) { + return !cfg.hasDefinedConfigs() && !cfg.hasExcludedConfigs() + && !cfg.hasExcludedPackages() && !cfg.hasFullModelsExcluded() + && !cfg.hasFullModelsIncluded() && !cfg.hasIncludedConfigs() + && !cfg.hasIncludedPackages() && !cfg.hasPatches() && cfg.isInheritPackages() + && cfg.isInheritConfigs(); + } + @Override public void undoAction(ProvisioningConfig.Builder builder) throws ProvisioningException { for (Entry entry : cf.entrySet()) { @@ -116,6 +137,13 @@ public void undoAction(ProvisioningConfig.Builder builder) throws ProvisioningEx builder.addFeaturePackDep(index, entry.getKey()); } } + for (FeaturePackConfig t : transitives) { + // Empty transitive are not added, so could no more exist + if (builder.hasTransitiveFeaturePackDep(t.getLocation().getProducer())) { + builder.removeTransitiveDep(t.getLocation().getFPID()); + } + builder.addFeaturePackDep(t); + } } } @@ -241,6 +269,51 @@ protected boolean doAction(FeaturePackConfig.Builder fpBuilder, String pkg) thro } } + private class ExcludePackageFromNewTransitiveAction implements State.Action { + + private final String pkg; + private final FeaturePackLocation loc; + ExcludePackageFromNewTransitiveAction(ProducerSpec producer, String pkg) { + this.pkg = pkg; + // New transitive are created without build ID. + loc = FeaturePackLocation.fromString(producer.toString()); + } + + @Override + public void doAction(ProvisioningConfig current, ProvisioningConfig.Builder builder) throws ProvisioningException { + FeaturePackConfig config = FeaturePackConfig.transitiveBuilder(loc).excludePackage(pkg).build(); + builder.addFeaturePackDep(config); + } + + @Override + public void undoAction(ProvisioningConfig.Builder builder) throws ProvisioningException { + builder.removeTransitiveDep(loc.getFPID()); + } + } + + private class IncludePackageInNewTransitiveAction implements State.Action { + + private final String pkg; + private final FeaturePackLocation loc; + + IncludePackageInNewTransitiveAction(ProducerSpec producer, String pkg) { + this.pkg = pkg; + // New transitive are created without build ID. + loc = FeaturePackLocation.fromString(producer.toString()); + } + + @Override + public void doAction(ProvisioningConfig current, ProvisioningConfig.Builder builder) throws ProvisioningException { + FeaturePackConfig config = FeaturePackConfig.transitiveBuilder(loc).includePackage(pkg).build(); + builder.addFeaturePackDep(config); + } + + @Override + public void undoAction(ProvisioningConfig.Builder builder) throws ProvisioningException { + builder.removeTransitiveDep(loc.getFPID()); + } + } + private class RemoveIncludedPackageAction extends AbstractAction { RemoveIncludedPackageAction(Map cf) { @@ -326,4 +399,12 @@ State.Action excludePackage(String pkg, FeaturePackConfig cf) throws Provisionin State.Action removeExcludedPackage(Map cf) throws ProvisioningDescriptionException, ProvisioningException, IOException { return new RemoveExcludedPackageAction(cf); } + + State.Action excludePackageFromNewTransitive(ProducerSpec producer, String pkg) { + return new ExcludePackageFromNewTransitiveAction(producer, pkg); + } + + State.Action includePackageInNewTransitive(ProducerSpec producer, String pkg) { + return new IncludePackageInNewTransitiveAction(producer, pkg); + } } diff --git a/cli/src/main/java/org/jboss/galleon/cli/model/state/State.java b/cli/src/main/java/org/jboss/galleon/cli/model/state/State.java index f6e4f6972..8862bb62b 100644 --- a/cli/src/main/java/org/jboss/galleon/cli/model/state/State.java +++ b/cli/src/main/java/org/jboss/galleon/cli/model/state/State.java @@ -45,6 +45,7 @@ import org.jboss.galleon.runtime.FeaturePackRuntime; import org.jboss.galleon.runtime.ProvisioningRuntime; import org.jboss.galleon.runtime.ProvisioningRuntimeBuilder; +import org.jboss.galleon.universe.FeaturePackLocation.ProducerSpec; import org.jboss.galleon.xml.ProvisioningXmlParser; import org.jboss.galleon.xml.ProvisioningXmlWriter; @@ -250,6 +251,16 @@ public void defineConfiguration(PmSession pmSession, ConfigId id) throws Provisi config = pushState(action, pmSession); } + public void excludePackageFromNewTransitive(PmSession pmSession, ProducerSpec producer, String pkg) throws ProvisioningException, IOException { + Action action = fpProvisioning.excludePackageFromNewTransitive(producer, pkg); + config = pushState(action, pmSession); + } + + public void includePackageInNewTransitive(PmSession pmSession, ProducerSpec producer, String pkg) throws ProvisioningException, IOException { + Action action = fpProvisioning.includePackageInNewTransitive(producer, pkg); + config = pushState(action, pmSession); + } + public void export(Path file) throws Exception { ProvisioningXmlWriter.getInstance().write(config, file); } diff --git a/core/src/main/java/org/jboss/galleon/config/FeaturePackDepsConfigBuilder.java b/core/src/main/java/org/jboss/galleon/config/FeaturePackDepsConfigBuilder.java index 45916a57c..67fa86a17 100644 --- a/core/src/main/java/org/jboss/galleon/config/FeaturePackDepsConfigBuilder.java +++ b/core/src/main/java/org/jboss/galleon/config/FeaturePackDepsConfigBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -152,6 +152,10 @@ public boolean hasFeaturePackDep(ProducerSpec producer) { return fpDeps.containsKey(producer); } + public boolean hasTransitiveFeaturePackDep(ProducerSpec producer) { + return transitiveDeps.containsKey(producer); + } + public boolean hasFeaturePackDeps() { return !fpDeps.isEmpty(); } diff --git a/testsuite/src/test/java/org/jboss/galleon/cli/CliTestUtils.java b/testsuite/src/test/java/org/jboss/galleon/cli/CliTestUtils.java index cecd1e5da..0bc32ca08 100644 --- a/testsuite/src/test/java/org/jboss/galleon/cli/CliTestUtils.java +++ b/testsuite/src/test/java/org/jboss/galleon/cli/CliTestUtils.java @@ -52,6 +52,7 @@ public abstract class CliTestUtils { public static final String PRODUCER1 = "producer1"; public static final String PRODUCER2 = "producer2"; public static final String PRODUCER3 = "producer3"; + public static final String PRODUCER4 = "producer4"; public static final String UNIVERSE_NAME = "cli-test-universe"; public static ProvisioningConfig getConfig(Path dir) throws ProvisioningException { diff --git a/testsuite/src/test/java/org/jboss/galleon/cli/StateTestCase.java b/testsuite/src/test/java/org/jboss/galleon/cli/StateTestCase.java index a58164142..9bd7674df 100644 --- a/testsuite/src/test/java/org/jboss/galleon/cli/StateTestCase.java +++ b/testsuite/src/test/java/org/jboss/galleon/cli/StateTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 Red Hat, Inc. and/or its affiliates + * Copyright 2016-2019 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,12 +20,17 @@ import java.util.Arrays; import org.aesh.command.CommandException; import org.jboss.galleon.ProvisioningException; +import org.jboss.galleon.ProvisioningManager; import static org.jboss.galleon.cli.CliTestUtils.PRODUCER1; import static org.jboss.galleon.cli.CliTestUtils.PRODUCER2; +import static org.jboss.galleon.cli.CliTestUtils.PRODUCER3; +import static org.jboss.galleon.cli.CliTestUtils.PRODUCER4; import static org.jboss.galleon.cli.CliTestUtils.UNIVERSE_NAME; import org.jboss.galleon.cli.path.PathParser; import org.jboss.galleon.config.ConfigModel; import org.jboss.galleon.config.FeatureConfig; +import org.jboss.galleon.config.FeaturePackConfig; +import org.jboss.galleon.config.ProvisioningConfig; import org.jboss.galleon.creator.FeaturePackCreator; import org.jboss.galleon.spec.ConfigLayerSpec; import org.jboss.galleon.spec.FeatureParameterSpec; @@ -49,16 +54,21 @@ public class StateTestCase { private static MvnUniverse universe; private static FeaturePackLocation loc; private static FeaturePackLocation locLayers; + private static FeaturePackLocation locWithTransitive; + private static FeaturePackLocation transitive; @BeforeClass public static void setup() throws Exception { cli = new CliWrapper(); universe = MvnUniverse.getInstance(UNIVERSE_NAME, cli.getSession().getMavenRepoManager()); - universeSpec = CliTestUtils.setupUniverse(universe, cli, UNIVERSE_NAME, Arrays.asList(PRODUCER1, PRODUCER2)); + universeSpec = CliTestUtils.setupUniverse(universe, cli, UNIVERSE_NAME, Arrays.asList(PRODUCER1, PRODUCER2, PRODUCER3, PRODUCER4)); install("1.0.0.Final"); installLayers("1.0.0.Final"); + installWithDependency("1.0.0.Final"); loc = CliTestUtils.buildFPL(universeSpec, PRODUCER1, "1", null, null); locLayers = CliTestUtils.buildFPL(universeSpec, PRODUCER2, "1", null, null); + locWithTransitive = CliTestUtils.buildFPL(universeSpec, PRODUCER4, "1", null, null); + transitive = CliTestUtils.buildFPL(universeSpec, PRODUCER3, "1", null, "1.0.0.Final"); } @AfterClass @@ -173,6 +183,182 @@ public void testNew() throws Exception { } } + @Test + public void testTransitivePackages() throws Exception { + cli.execute("state new"); + try { + cli.execute("add-dependency " + locWithTransitive + " --default-configs-inherit"); + cli.execute("get-info --type=optional-packages"); + Assert.assertTrue(cli.getOutput(), cli.getOutput().contains("p1")); + Assert.assertTrue(cli.getOutput(), cli.getOutput().contains(PRODUCER3)); + cli.execute("exclude-package " + transitive.getProducer() + "/p1"); + cli.execute("get-info --type=optional-packages"); + Assert.assertFalse(cli.getOutput(), cli.getOutput().contains("p1")); + Assert.assertFalse(cli.getOutput(), cli.getOutput().contains(PRODUCER3)); + + Path p1 = cli.newDir("exported-transitive1", false); + cli.execute("provision --dir=" + p1.toFile().getAbsolutePath()); + ProvisioningManager mgr1 = ProvisioningManager.builder().setInstallationHome(p1).build(); + Assert.assertTrue(mgr1.getProvisioningConfig().getTransitiveDeps().size() == 1); + Assert.assertTrue(mgr1.getProvisioningConfig().getTransitiveDeps().iterator().next(). + getExcludedPackages().contains("p1")); + cli.execute("undo"); + cli.execute("get-info --type=optional-packages"); + Assert.assertTrue(cli.getOutput(), cli.getOutput().contains("p1")); + Assert.assertTrue(cli.getOutput(), cli.getOutput().contains(PRODUCER3)); + + cli.execute("exclude-package " + transitive.getProducer() + "/p1"); + cli.execute("get-info --type=optional-packages"); + Assert.assertFalse(cli.getOutput(), cli.getOutput().contains("p1")); + Assert.assertFalse(cli.getOutput(), cli.getOutput().contains(PRODUCER3)); + cli.execute("remove-excluded-package p1"); + cli.execute("get-info --type=optional-packages"); + Assert.assertTrue(cli.getOutput(), cli.getOutput().contains("p1")); + Assert.assertTrue(cli.getOutput(), cli.getOutput().contains(PRODUCER3)); + cli.execute("undo"); + cli.execute("get-info --type=optional-packages"); + Assert.assertFalse(cli.getOutput(), cli.getOutput().contains("p1")); + Assert.assertFalse(cli.getOutput(), cli.getOutput().contains(PRODUCER3)); + + cli.execute("remove-excluded-package p1"); + Path p2 = cli.newDir("exported-transitive2", false); + cli.execute("provision --dir=" + p2.toFile().getAbsolutePath()); + ProvisioningManager mgr2 = ProvisioningManager.builder().setInstallationHome(p2).build(); + Assert.assertTrue(mgr2.getProvisioningConfig().getTransitiveDeps().isEmpty()); + + boolean failed = false; + try { + cli.execute("ls /packages/" + transitive.getProducer() + "/p2"); + } catch (Exception ex) { + // expected. + failed = true; + } + if (!failed) { + throw new Exception("Package p2 shouldn't be present"); + } + cli.execute("include-package " + transitive.getProducer() + "/p2"); + + Path p3 = cli.newDir("exported-transitive3", false); + cli.execute("provision --dir=" + p3.toFile().getAbsolutePath()); + ProvisioningManager mgr3 = ProvisioningManager.builder().setInstallationHome(p3).build(); + Assert.assertTrue(mgr3.getProvisioningConfig().getTransitiveDeps().size() == 1); + Assert.assertTrue(mgr3.getProvisioningConfig().getTransitiveDeps().iterator().next(). + getIncludedPackages().contains("p2")); + cli.execute("ls /packages/" + transitive.getProducer() + "/p2"); + cli.execute("undo"); + failed = false; + try { + cli.execute("ls /packages/" + transitive.getProducer() + "/p2"); + } catch (Exception ex) { + // expected. + failed = true; + } + if (!failed) { + throw new Exception("Package p2 shouldn't be present"); + } + cli.execute("include-package " + transitive.getProducer() + "/p2"); + + cli.execute("remove-included-package p2"); + failed = false; + try { + cli.execute("ls /packages/" + transitive.getProducer() + "/p2"); + } catch (Exception ex) { + // expected. + failed = true; + } + if (!failed) { + throw new Exception("Package p2 shouldn't be present"); + } + cli.execute("undo"); + cli.execute("ls /packages/" + transitive.getProducer() + "/p2"); + cli.execute("remove-included-package p2"); + + Path p4 = cli.newDir("exported-transitive4", false); + cli.execute("provision --dir=" + p4.toFile().getAbsolutePath()); + ProvisioningManager mgr4 = ProvisioningManager.builder().setInstallationHome(p4).build(); + Assert.assertTrue(mgr4.getProvisioningConfig().getTransitiveDeps().isEmpty()); + } finally { + cli.execute("leave-state"); + } + } + + @Test + public void testTransitiveWithVersion() throws Exception { + ProvisioningConfig config = ProvisioningConfig.builder().addFeaturePackDep(locWithTransitive). + addTransitiveDep(transitive).build(); + Path p = cli.newDir("version_transitive", false); + //Provision a config with a transitive with version set. + ProvisioningManager mgr1 = cli.getSession().newProvisioningManager(p, true); + mgr1.provision(config); + cli.execute("state edit " + p.toFile().getAbsolutePath()); + try { + cli.execute("get-info --type=optional-packages"); + Assert.assertTrue(cli.getOutput(), cli.getOutput().contains("p1")); + Assert.assertTrue(cli.getOutput(), cli.getOutput().contains(PRODUCER3)); + cli.execute("exclude-package " + transitive.getProducer() + "/p1"); + cli.execute("get-info --type=optional-packages"); + Assert.assertFalse(cli.getOutput(), cli.getOutput().contains("p1")); + Assert.assertFalse(cli.getOutput(), cli.getOutput().contains(PRODUCER3)); + cli.execute("undo"); + // Check that we still have the transitive dep. + ProvisioningManager mgr2 = ProvisioningManager.builder().setInstallationHome(p).build(); + Assert.assertTrue(mgr2.getProvisioningConfig().getTransitiveDeps().size() == 1); + Assert.assertTrue(mgr2.getProvisioningConfig().getTransitiveDeps().iterator().next(). + getExcludedPackages().isEmpty()); + } finally { + cli.execute("leave-state"); + } + } + + @Test + public void testTransitiveWithExistingPackages() throws Exception { + FeaturePackConfig transitiveConfig = FeaturePackConfig.transitiveBuilder(transitive). + excludePackage("p1").includePackage("p2").build(); + FeaturePackConfig topLevelConfig = FeaturePackConfig.builder(locWithTransitive).setInheritPackages(false).build(); + ProvisioningConfig config = ProvisioningConfig.builder().addFeaturePackDep(topLevelConfig). + addFeaturePackDep(transitiveConfig).build(); + Path p = cli.newDir("transitive_packages", false); + //Provision a config with a transitive with already excluded package + ProvisioningManager mgr1 = cli.getSession().newProvisioningManager(p, true); + mgr1.provision(config); + cli.execute("state edit " + p.toFile().getAbsolutePath()); + try { + cli.execute("get-info --type=optional-packages"); + Assert.assertFalse(cli.getOutput(), cli.getOutput().contains("p1")); + Assert.assertFalse(cli.getOutput(), cli.getOutput().contains(PRODUCER3)); + cli.execute("remove-excluded-package p1"); + cli.execute("get-info --type=optional-packages"); + Assert.assertTrue(cli.getOutput(), cli.getOutput().contains("p1")); + Assert.assertTrue(cli.getOutput(), cli.getOutput().contains(PRODUCER3)); + cli.execute("undo"); + + cli.execute("ls /packages/" + transitive.getProducer() + "/p2"); + cli.execute("remove-included-package p2"); + boolean failed = false; + try { + cli.execute("ls /packages/" + transitive.getProducer() + "/p2"); + } catch (Exception ex) { + // expected. + failed = true; + } + if (!failed) { + throw new Exception("Package p2 shouldn't be present"); + } + cli.execute("undo"); + Path p2 = cli.newDir("transitive_packages_reprovisioned", false); + cli.execute("provision --dir=" + p2.toFile().getAbsolutePath()); + // Check that we still have the transitive dep and its original content. + ProvisioningManager mgr2 = ProvisioningManager.builder().setInstallationHome(p2).build(); + Assert.assertTrue(mgr2.getProvisioningConfig().getTransitiveDeps().size() == 1); + Assert.assertTrue(mgr2.getProvisioningConfig().getTransitiveDeps().iterator().next(). + getExcludedPackages().contains("p1")); + Assert.assertTrue(mgr2.getProvisioningConfig().getTransitiveDeps().iterator().next(). + getIncludedPackages().contains("p2")); + } finally { + cli.execute("leave-state"); + } + } + @Test public void testLayers() throws Exception { cli.execute("state new"); @@ -388,6 +574,34 @@ public static void install(String version) throws ProvisioningException { creator.install(); } + public static void installWithDependency(String version) throws ProvisioningException { + FeaturePackCreator creator = FeaturePackCreator.getInstance().addArtifactResolver(cli.getSession().getMavenRepoManager()); + FeaturePackLocation fp1 = new FeaturePackLocation(universeSpec, + PRODUCER3, "1", null, version); + creator.newFeaturePack(fp1.getFPID()).addFeatureSpec(FeatureSpec.builder("specA").addPackageDep("p1", true) + .addParam(FeatureParameterSpec.createId("p1")) + .build()) + .addConfig(ConfigModel.builder().setModel("model1"). + setName("name1"). + addFeature(new FeatureConfig("specA").setParam("p1", "1")).build(), true) + .newPackage("p1", false) + .writeContent("fp1/p1.txt", "fp1 p1").getFeaturePack(). + newPackage("p2", true) + .writeContent("fp2/p2.txt", "fp1 p2"); + + FeaturePackLocation fp2 = new FeaturePackLocation(universeSpec, + PRODUCER4, "1", null, version); + FeaturePackConfig dep = FeaturePackConfig.builder(fp1).setInheritConfigs(false).setInheritPackages(false).build(); + creator.newFeaturePack(fp2.getFPID()) + .addDependency(dep).addFeatureSpec(FeatureSpec.builder("specB") + .addParam(FeatureParameterSpec.createId("p1")) + .build()). + addConfig(ConfigModel.builder().setModel("model1"). + setName("name1").addFeature(new FeatureConfig("specB").setParam("p1", "1")). + addFeature(new FeatureConfig("specA").setParam("p1", "1")).build(), true); + creator.install(); + } + public static void installLayers(String version) throws ProvisioningException { FeaturePackCreator creator = FeaturePackCreator.getInstance().addArtifactResolver(cli.getSession().getMavenRepoManager()); FeaturePackLocation fp1 = new FeaturePackLocation(universeSpec,