docs: public void installFeatures(Collection<String> featureNames, String toExtension, boolean acceptLicense, boolean allowAlreadyInstalled, String userId, String password,
                                  int checkProgress) throws InstallException {
          fireProgressEvent(InstallProgressEvent.CHECK, checkProgress, Messages.INSTALL_KERNEL_MESSAGES.getLogMessage("STATE_CHECKING"));
  
          if (featureNames == null || featureNames.isEmpty()) {
              throw ExceptionUtils.createByKey("ERROR_FEATURES_LIST_INVALID");
          }
  
          List<List<RepositoryResource>> installResources = getResolveDirector().resolve(featureNames, DownloadOption.required, userId, password);
          if (isEmpty(installResources)) {
              if (allowAlreadyInstalled)
                  return;
              throw ExceptionUtils.createByKey(InstallException.ALREADY_EXISTS, "ALREADY_INSTALLED",
                                               InstallUtils.getShortNames(product.getFeatureDefinitions(), featureNames).toString());
          }
  
          if (!acceptLicense) {
              throw ExceptionUtils.createByKey("ERROR_LICENSES_NOT_ACCEPTED");
          }
  
          this.installAssets = new ArrayList<List<InstallAsset>>(installResources.size());
          int progress = 10;
          int interval1 = installResources.size() == 0 ? 40 : 40 / installResources.size();
          for (List<RepositoryResource> mrList : installResources) {
              List<InstallAsset> iaList = new ArrayList<InstallAsset>(mrList.size());
              installAssets.add(iaList);
              int interval2 = mrList.size() == 0 ? interval1 : interval1 / mrList.size();
              for (RepositoryResource installResource : mrList) {
                  fireDownloadProgressEvent(progress, installResource);
                  progress += interval2;
                  File d = null;
                  try {
                      d = InstallUtils.download(this.product.getInstallTempDir(), installResource);
                      if (d != null && installResource.getType().equals(ResourceType.FEATURE)) {
                          Visibility v = ((EsaResource) installResource).getVisibility();
                          if (v.equals(Visibility.PUBLIC) || v.equals(Visibility.INSTALL)) {
                              log(Level.FINE, Messages.INSTALL_KERNEL_MESSAGES.getLogMessage("MSG_DOWNLOAD_SUCCESS", installResource.getName()));
                          }
                      }
                      log(Level.FINEST, d == null ? installResource.getName() + " is an unsupported type " + installResource.getType()
                                                    + " to be downloaded." : "Downloaded " + installResource.getName() + " to " + d.getAbsolutePath());
                  } catch (InstallException e) {
                      throw e;
                  } catch (Exception e) {
                      throw ExceptionUtils.createFailedToDownload(installResource, e, (d == null ? this.product.getInstallTempDir() : d));
                  }
                  if (installResource.getType().equals(ResourceType.FEATURE)) {
                      EsaResource esa = (EsaResource) installResource;
                      ESAAsset esaAsset;
                      try {
                          esaAsset = new ESAAsset(esa.getName(), esa.getProvideFeature(), toExtension, d, true);
                          if (esaAsset.getSubsystemEntry() == null) {
                              throw ExceptionUtils.create(Messages.PROVISIONER_MESSAGES.getLogMessage("tool.install.content.no.subsystem.manifest"),
                                                          InstallException.BAD_FEATURE_DEFINITION);
                          }
                          ProvisioningFeatureDefinition fd = esaAsset.getProvisioningFeatureDefinition();
                          if (!fd.isSupportedFeatureVersion()) {
                              throw ExceptionUtils.create(Messages.PROVISIONER_MESSAGES.getLogMessage("UNSUPPORTED_FEATURE_VERSION", fd.getFeatureName(),
                                                                                                      fd.getIbmFeatureVersion()),
                                                          InstallException.BAD_FEATURE_DEFINITION);
                          }
                          iaList.add(esaAsset);
                      } catch (Exception e) {
                          throw ExceptionUtils.createByKey(e, "ERROR_INVALID_ESA", esa.getName());
                      }
                  } else if (installResource.getType().equals(ResourceType.IFIX)) {
                      try {
                          iaList.add(new FixAsset(installResource.getName(), d, true));
                      } catch (Exception e) {
                          throw ExceptionUtils.createByKey(e, "ERROR_INVALID_IFIX", installResource.getName());
                      }
                  }
              }
          }
      }