code: public void installFeature(Collection<String> featureIds, File fromDir, String toExtension, boolean acceptLicense, boolean offlineOnly) throws InstallException {
          //fireProgressEvent(InstallProgressEvent.CHECK, 1, Messages.INSTALL_KERNEL_MESSAGES.getLogMessage("STATE_CHECKING"));
          this.installAssets = new ArrayList<List<InstallAsset>>();
          ArrayList<InstallAsset> installAssets = new ArrayList<InstallAsset>();
          ArrayList<String> unresolvedFeatures = new ArrayList<String>();
          Collection<ESAAsset> autoFeatures = getResolveDirector().getAutoFeature(fromDir, toExtension);
          getResolveDirector().resolve(featureIds, fromDir, toExtension, offlineOnly, installAssets, unresolvedFeatures);
          if (!offlineOnly && !unresolvedFeatures.isEmpty()) {
              log(Level.FINEST, "installFeature() determined unresolved features: " + unresolvedFeatures.toString() + " from " + fromDir.getAbsolutePath());
              installFeatures(unresolvedFeatures, toExtension, acceptLicense, null, null, 5);
          }
          if (!installAssets.isEmpty()) {
              getResolveDirector().resolveAutoFeatures(autoFeatures, installAssets);
              this.installAssets.add(installAssets);
          }
          if (this.installAssets.isEmpty()) {
              throw ExceptionUtils.createByKey(InstallException.ALREADY_EXISTS, "ALREADY_INSTALLED", featureIds.toString());
          }
      }
docs: Installs the features found in the inputed featureIds collection
  
  @param featureIds the feature ids
  @param fromDir where the features are located
  @param toExtension location of a product extension
  @param acceptLicense if license is accepted
  @param offlineOnly if features should be installed from local source only
  @throws InstallException