private void processSubsystemConfigInFeaturePack(
      ServerProvisioningFeaturePack provisioningFeaturePack,
      ServerProvisioning serverProvisioning,
      ArtifactFileResolver artifactFileResolver)
      throws IOException {

    File artifactFile =
        artifactFileResolver.getArtifactFile(
            provisioningFeaturePack.getFeaturePack().getArtifact());
    // features packs themselves can contain a 'subsystem-templates' directory. Templates in the
    // feature pack override ones from modules
    serverProvisioning
        .getConfig()
        .getInputStreamSources()
        .addAllSubsystemFileSourcesFromZipFile(artifactFile);
  }
  public void build() {
    final ServerProvisioning serverProvisioning = new ServerProvisioning(description);
    final List<String> errors = new ArrayList<>();
    try {
      // create the feature packs
      for (ServerProvisioningDescription.FeaturePack serverProvisioningFeaturePackDescription :
          description.getFeaturePacks()) {
        final FeaturePack featurePack =
            FeaturePackFactory.createPack(
                serverProvisioningFeaturePackDescription.getArtifact(),
                artifactFileResolver,
                versionOverrideArtifactResolver);
        serverProvisioning
            .getFeaturePacks()
            .add(
                new ServerProvisioningFeaturePack(
                    serverProvisioningFeaturePackDescription, featurePack, artifactFileResolver));
      }
      // create output dir
      FileUtils.deleteRecursive(outputDirectory);
      outputDirectory.mkdirs();
      // create schema output dir if needed
      final File schemaOutputDirectory;
      if (description.isExtractSchemas()) {
        schemaOutputDirectory = new File(outputDirectory, SUBSYSTEM_SCHEMA_TARGET_DIRECTORY);
        if (!schemaOutputDirectory.exists()) {
          schemaOutputDirectory.mkdirs();
        }
      } else {
        schemaOutputDirectory = null;
      }
      final Set<String> filesProcessed = new HashSet<>();
      // process server provisioning copy-artifacts
      processCopyArtifacts(
          serverProvisioning.getDescription().getCopyArtifacts(),
          versionOverrideArtifactResolver,
          outputDirectory,
          filesProcessed,
          artifactFileResolver,
          schemaOutputDirectory);
      // process modules (needs to be done for all feature packs before any config is processed, due
      // to subsystem template gathering)
      processModules(
          serverProvisioning,
          outputDirectory,
          filesProcessed,
          artifactFileResolver,
          schemaOutputDirectory);

      // process everything else for each feature pack
      for (ServerProvisioningFeaturePack provisioningFeaturePack :
          serverProvisioning.getFeaturePacks()) {
        processSubsystemConfigInFeaturePack(
            provisioningFeaturePack, serverProvisioning, artifactFileResolver);
        processFeaturePackCopyArtifacts(
            provisioningFeaturePack.getFeaturePack(),
            outputDirectory,
            filesProcessed,
            artifactFileResolver,
            schemaOutputDirectory,
            description.isExcludeDependencies());
        processProvisioningFeaturePackContents(
            provisioningFeaturePack,
            outputDirectory,
            filesProcessed,
            description.isExcludeDependencies());
        processFeaturePackFilePermissions(
            provisioningFeaturePack.getFeaturePack(),
            outputDirectory,
            description.isExcludeDependencies());
      }
      // process the server config
      processConfig(serverProvisioning, outputDirectory, filesProcessed);
    } catch (Throwable e) {
      throw new RuntimeException(e);
    } finally {
      if (!errors.isEmpty()) {
        StringBuilder sb = new StringBuilder();
        sb.append("Some errors were encountered creating the feature pack\n");
        for (String error : errors) {
          sb.append(error);
          sb.append("\n");
        }
        throw new RuntimeException(sb.toString());
      }
    }
  }
 private void processConfig(
     ServerProvisioning serverProvisioning, File outputDirectory, Set<String> filesProcessed)
     throws IOException, XMLStreamException {
   ServerProvisioning.Config provisioningConfig = serverProvisioning.getConfig();
   // 1. collect and merge each feature pack configs
   for (ServerProvisioningFeaturePack provisioningFeaturePack :
       serverProvisioning.getFeaturePacks()) {
     processFeaturePackConfig(provisioningFeaturePack, provisioningConfig);
   }
   // 2. assemble the merged configs
   for (ServerProvisioning.ConfigFile provisioningConfigFile :
       provisioningConfig.getDomainConfigFiles().values()) {
     if (provisioningConfigFile.getTemplateInputStreamSource() == null) {
       getLog()
           .debugf(
               "Skipping assembly of config file %s, template not set.",
               provisioningConfigFile.getOutputFile());
       continue;
     }
     getLog().debugf("Assembling config file %s", provisioningConfigFile.getOutputFile());
     filesProcessed.add(provisioningConfigFile.getOutputFile());
     new ConfigurationAssembler(
             provisioningConfig.getInputStreamSources(),
             provisioningConfigFile.getTemplateInputStreamSource(),
             "domain",
             provisioningConfigFile.getSubsystems(),
             new File(outputDirectory, provisioningConfigFile.getOutputFile()))
         .assemble();
   }
   for (ServerProvisioning.ConfigFile provisioningConfigFile :
       provisioningConfig.getStandaloneConfigFiles().values()) {
     if (provisioningConfigFile.getTemplateInputStreamSource() == null) {
       getLog()
           .debugf(
               "Skipping assembly of config file %s, template not set.",
               provisioningConfigFile.getOutputFile());
       continue;
     }
     getLog().debugf("Assembling config file %s", provisioningConfigFile.getOutputFile());
     filesProcessed.add(provisioningConfigFile.getOutputFile());
     new ConfigurationAssembler(
             provisioningConfig.getInputStreamSources(),
             provisioningConfigFile.getTemplateInputStreamSource(),
             "server",
             provisioningConfigFile.getSubsystems(),
             new File(outputDirectory, provisioningConfigFile.getOutputFile()))
         .assemble();
   }
   for (ServerProvisioning.ConfigFile provisioningConfigFile :
       provisioningConfig.getHostConfigFiles().values()) {
     if (provisioningConfigFile.getTemplateInputStreamSource() == null) {
       getLog()
           .debugf(
               "Skipping assembly of config file %s, template not set.",
               provisioningConfigFile.getOutputFile());
       continue;
     }
     getLog().debugf("Assembling config file %s", provisioningConfigFile.getOutputFile());
     filesProcessed.add(provisioningConfigFile.getOutputFile());
     new ConfigurationAssembler(
             provisioningConfig.getInputStreamSources(),
             provisioningConfigFile.getTemplateInputStreamSource(),
             "host",
             provisioningConfigFile.getSubsystems(),
             new File(outputDirectory, provisioningConfigFile.getOutputFile()))
         .assemble();
   }
 }
  private void processFeaturePackModules(
      FeaturePack featurePack,
      List<FeaturePack.Module> includedModules,
      ServerProvisioning serverProvisioning,
      File outputDirectory,
      Set<String> filesProcessed,
      ArtifactFileResolver artifactFileResolver,
      File schemaOutputDirectory)
      throws IOException {
    final boolean thinServer = !serverProvisioning.getDescription().isCopyModuleArtifacts();
    // create the module's artifact property replacer
    final BuildPropertyReplacer buildPropertyReplacer =
        thinServer
            ? new BuildPropertyReplacer(
                new ModuleArtifactPropertyResolver(featurePack.getArtifactResolver()))
            : null;
    // process each module file
    try (JarFile jar = new JarFile(featurePack.getFeaturePackFile())) {
      for (FeaturePack.Module module : includedModules) {
        // process the module file
        final String jarEntryName = module.getModuleFile();
        filesProcessed.add(jarEntryName);
        File targetFile = new File(outputDirectory, jarEntryName);
        // ensure parent dirs exist
        targetFile.getParentFile().mkdirs();
        // extract the module file
        FileUtils.extractFile(jar, jarEntryName, targetFile);
        // read module xml to string for content update
        String moduleXmlContents = FileUtils.readFile(targetFile);
        // parse the module xml
        ModuleParseResult result = module.getModuleParseResult();
        // process module artifacts
        for (ModuleParseResult.ArtifactName artifactName : result.getArtifacts()) {
          String artifactCoords = artifactName.getArtifactCoords();
          String options = artifactName.getOptions();
          boolean jandex = false;
          if (options != null) {
            jandex =
                options.contains(
                    "jandex"); // todo: eventually we may need options to have a proper query string
                               // type syntax
            moduleXmlContents =
                moduleXmlContents.replace(
                    artifactName.toString(),
                    artifactCoords); // todo: all these replace calls are a bit yuck, we may need
                                     // proper solution if this gets more complex
          }
          Artifact artifact = featurePack.getArtifactResolver().getArtifact(artifactCoords);
          if (artifact == null) {
            throw new RuntimeException(
                "Could not resolve module resource artifact "
                    + artifactName
                    + " for feature pack "
                    + featurePack.getFeaturePackFile());
          }
          try {
            // process the module artifact
            File artifactFile = artifactFileResolver.getArtifactFile(artifact);
            // extract schemas if needed
            extractSchema(schemaOutputDirectory, artifact, artifactFile);
            if (jandex) {
              String baseName =
                  artifactFile.getName().substring(0, artifactFile.getName().lastIndexOf("."));
              String extension =
                  artifactFile.getName().substring(artifactFile.getName().lastIndexOf("."));
              File target = new File(targetFile.getParent(), baseName + "-jandex" + extension);
              JandexIndexer.createIndex(artifactFile, new FileOutputStream(target));
              moduleXmlContents =
                  moduleXmlContents.replaceAll(
                      "(\\s*)<artifact\\s+name=\"\\$\\{" + artifactCoords + "\\}\"\\s*/>",
                      "$1<artifact name=\"\\${"
                          + artifactCoords
                          + "}\" />$1<resource-root path=\""
                          + target.getName()
                          + "\"/>");
              // it's also possible that this is an element with nested content
              // this regex involves a good deal of backtracking but it seems to work
              moduleXmlContents =
                  Pattern.compile(
                          "(\\s*)<artifact\\s+name=\"\\$\\{"
                              + artifactCoords
                              + "\\}\"\\s*>(.*)</artifact>",
                          Pattern.DOTALL)
                      .matcher(moduleXmlContents)
                      .replaceAll(
                          "$1<artifact name=\"\\${"
                              + artifactCoords
                              + "}\">$2</artifact>$1<resource-root path=\""
                              + target.getName()
                              + "\">$2</resource-root>");
            }
            if (!thinServer) {
              // copy the artifact
              String artifactFileName = artifactFile.getName();
              FileUtils.copyFile(artifactFile, new File(targetFile.getParent(), artifactFileName));
              // update module xml content
              moduleXmlContents =
                  moduleXmlContents.replaceAll(
                      "<artifact\\s+name=\"\\$\\{" + artifactCoords + "\\}\"\\s*/>",
                      "<resource-root path=\"" + artifactFileName + "\"/>");
              // it's also possible that this is an element with nested content
              // this regex involves a good deal of backtracking but it seems to work
              moduleXmlContents =
                  Pattern.compile(
                          "<artifact\\s+name=\"\\$\\{"
                              + artifactCoords
                              + "\\}\"\\s*>(.*)</artifact>",
                          Pattern.DOTALL)
                      .matcher(moduleXmlContents)
                      .replaceAll(
                          "<resource-root path=\"" + artifactFileName + "\">$1</resource-root>");
            }
          } catch (Throwable t) {
            throw new RuntimeException("Could not extract resources from " + artifactName, t);
          }
        }
        if (thinServer) {
          // replace artifact coords properties with the ones expected by jboss-modules
          moduleXmlContents = buildPropertyReplacer.replaceProperties(moduleXmlContents);
        }
        // write updated module xml content
        FileUtils.copyFile(
            new ByteArrayInputStream(moduleXmlContents.getBytes("UTF-8")), targetFile);

        // extract all other files in the module dir
        for (String moduleDirFile : module.getModuleDirFiles()) {
          filesProcessed.add(moduleDirFile);
          FileUtils.extractFile(jar, moduleDirFile, new File(outputDirectory, moduleDirFile));
        }
      }
    } catch (Throwable e) {
      throw new RuntimeException(
          "Failed to process feature pack " + featurePack.getFeaturePackFile() + " modules", e);
    }
  }
 private void processModules(
     ServerProvisioning serverProvisioning,
     File outputDirectory,
     Set<String> filesProcessed,
     ArtifactFileResolver artifactFileResolver,
     File schemaOutputDirectory)
     throws IOException, XMLStreamException {
   // 1. gather the modules for each feature pack
   final Map<FeaturePack, List<FeaturePack.Module>> featurePackModulesMap = new HashMap<>();
   Set<ModuleIdentifier> moduleIdentifiers = new HashSet<>();
   for (ServerProvisioningFeaturePack provisioningFeaturePack :
       serverProvisioning.getFeaturePacks()) {
     getLog()
         .debugf(
             "Gathering modules for provisioning feature pack %s",
             provisioningFeaturePack.getFeaturePack().getFeaturePackFile());
     for (FeaturePack.Module module :
         provisioningFeaturePack
             .getModules(
                 artifactFileResolver, serverProvisioning.getDescription().isExcludeDependencies())
             .values()) {
       final ModuleIdentifier moduleIdentifier = module.getIdentifier();
       if (moduleIdentifiers.add(moduleIdentifier)) {
         getLog()
             .debugf(
                 "Adding module %s from feature pack %s",
                 moduleIdentifier, module.getFeaturePack().getFeaturePackFile());
         List<FeaturePack.Module> featurePackModules =
             featurePackModulesMap.get(module.getFeaturePack());
         if (featurePackModules == null) {
           featurePackModules = new ArrayList<>();
           featurePackModulesMap.put(module.getFeaturePack(), featurePackModules);
         }
         featurePackModules.add(module);
       } else {
         getLog()
             .debugf(
                 "Skipping %s from feature pack %s. A module with such identifier is already in the provisioning module set.",
                 moduleIdentifier, module.getFeaturePack().getFeaturePackFile());
       }
     }
     // we always need to resolve all subsystem templates, regardless of the value of
     // exclude-dependencies
     for (FeaturePack.Module module :
         provisioningFeaturePack.getModules(artifactFileResolver, false).values()) {
       for (ModuleParseResult.ArtifactName artifactName :
           module.getModuleParseResult().getArtifacts()) {
         String artifactCoords = artifactName.getArtifactCoords();
         String options = artifactName.getOptions();
         Artifact artifact =
             module.getFeaturePack().getArtifactResolver().getArtifact(artifactCoords);
         if (artifact == null) {
           throw new RuntimeException(
               "Could not resolve module resource artifact "
                   + artifactName
                   + " for feature pack "
                   + module.getFeaturePack().getFeaturePackFile());
         }
         File artifactFile = artifactFileResolver.getArtifactFile(artifact);
         // add all subsystem templates
         serverProvisioning
             .getConfig()
             .getInputStreamSources()
             .addAllSubsystemFileSourcesFromZipFile(artifactFile);
       }
     }
   }
   // 2. provision each feature pack modules
   for (Map.Entry<FeaturePack, List<FeaturePack.Module>> mapEntry :
       featurePackModulesMap.entrySet()) {
     FeaturePack featurePack = mapEntry.getKey();
     List<FeaturePack.Module> includedModules = mapEntry.getValue();
     processFeaturePackModules(
         featurePack,
         includedModules,
         serverProvisioning,
         outputDirectory,
         filesProcessed,
         artifactFileResolver,
         schemaOutputDirectory);
   }
 }