docs: public Module generateBuildInfoModule(Run build, TaskListener listener, ArtifactoryConfigurator config, String buildName, String buildNumber, String timestamp) throws IOException {
          if (artifactsProps == null) {
              artifactsProps = ArrayListMultimap.create();
          }
          artifactsProps.put("build.name", buildName);
          artifactsProps.put("build.number", buildNumber);
          artifactsProps.put("build.timestamp", timestamp);
          String artifactsPropsStr = ExtractorUtils.buildPropertiesString(artifactsProps);
  
          Properties buildInfoItemsProps = new Properties();
          buildInfoItemsProps.setProperty("build.name", buildName);
          buildInfoItemsProps.setProperty("build.number", buildNumber);
          buildInfoItemsProps.setProperty("build.timestamp", timestamp);
  
          ArtifactoryServer server = config.getArtifactoryServer();
          CredentialsConfig preferredResolver = server.getDeployerCredentialsConfig();
          ArtifactoryDependenciesClient dependenciesClient = null;
          ArtifactoryBuildInfoClient propertyChangeClient = null;
  
          try {
              dependenciesClient = server.createArtifactoryDependenciesClient(
                      preferredResolver.provideUsername(build.getParent()), preferredResolver.providePassword(build.getParent()),
                      server.createProxyConfiguration(Jenkins.getInstance().proxy), listener);
  
              CredentialsConfig preferredDeployer = CredentialManager.getPreferredDeployer(config, server);
              propertyChangeClient = server.createArtifactoryClient(
                      preferredDeployer.provideUsername(build.getParent()), preferredDeployer.providePassword(build.getParent()),
                      server.createProxyConfiguration(Jenkins.getInstance().proxy));
  
              Module buildInfoModule = new Module();
              buildInfoModule.setId(imageTag.substring(imageTag.indexOf("/") + 1));
  
              // If manifest and imagePath not found, return.
              if ((StringUtils.isEmpty(manifest) || StringUtils.isEmpty(imagePath)) && !findAndSetManifestFromArtifactory(server, dependenciesClient, listener)) {
                  return buildInfoModule;
              }
  
              listener.getLogger().println("Fetching details of published docker layers from Artifactory...");
              boolean includeVirtualReposSupported = propertyChangeClient.getArtifactoryVersion().isAtLeast(VIRTUAL_REPOS_SUPPORTED_VERSION);
              DockerLayers layers = createLayers(dependenciesClient, includeVirtualReposSupported);
  
              listener.getLogger().println("Tagging published docker layers with build properties in Artifactory...");
              setDependenciesAndArtifacts(buildInfoModule, layers, artifactsPropsStr, buildInfoItemsProps,
                      dependenciesClient, propertyChangeClient, server);
              setBuildInfoModuleProps(buildInfoModule);
              return buildInfoModule;
          } finally {
              if (dependenciesClient != null) {
                  dependenciesClient.close();
              }
              if (propertyChangeClient != null) {
                  propertyChangeClient.close();
              }
          }
      }