diff --git a/src/com/fivium/scriptrunner2/CommandLineOption.java b/src/com/fivium/scriptrunner2/CommandLineOption.java index 70d84f9..d9f879a 100644 --- a/src/com/fivium/scriptrunner2/CommandLineOption.java +++ b/src/com/fivium/scriptrunner2/CommandLineOption.java @@ -28,7 +28,8 @@ public enum CommandLineOption { , ADDITIONAL_PROPERTIES("props") , INSTALL_PROMOTE_USER("newpromoteuser") , INSTALL_PROMOTE_PASSWORD("newpromotepassword") - , NO_UNIMPLICATED_FILES("nounimplicatedfiles"); + , NO_UNIMPLICATED_FILES("nounimplicatedfiles") + , VERIFY_LOADERS("verifyloaders"); private final String mArgString; diff --git a/src/com/fivium/scriptrunner2/CommandLineWrapper.java b/src/com/fivium/scriptrunner2/CommandLineWrapper.java index c4a6abe..1a0af2e 100644 --- a/src/com/fivium/scriptrunner2/CommandLineWrapper.java +++ b/src/com/fivium/scriptrunner2/CommandLineWrapper.java @@ -89,6 +89,7 @@ public class CommandLineWrapper { gCommandLineOptions.addOption(CommandLineOption.ADDITIONAL_PROPERTIES.getArgString(), true, "(Build only) Location of the additional properties file for the builder."); gCommandLineOptions.addOption(CommandLineOption.NO_UNIMPLICATED_FILES.getArgString(), false, "(Build only) Error (rather than warn) if files are found in source directory but not implicated by manifest builder rules."); + gCommandLineOptions.addOption(CommandLineOption.VERIFY_LOADERS.getArgString(), false, "(Build only) Validate that the Loader files can be found at build time."); //gCommandLineOptions.addOption("help", false, "Prints help."); } diff --git a/src/com/fivium/scriptrunner2/Main.java b/src/com/fivium/scriptrunner2/Main.java index fc6f832..7c3910f 100644 --- a/src/com/fivium/scriptrunner2/Main.java +++ b/src/com/fivium/scriptrunner2/Main.java @@ -85,7 +85,8 @@ public static void main(String[] args) { } } else if(lCommandLineOptions.hasOption(CommandLineOption.BUILD)){ - ScriptBuilder.run(lCommandLineOptions); + ScriptBuilder lScriptBuilder = new ScriptBuilder(lCommandLineOptions); + lScriptBuilder.run(); Logger.logAndEcho("Build completed successfully"); } else if(lCommandLineOptions.hasOption(CommandLineOption.INSTALL)){ diff --git a/src/com/fivium/scriptrunner2/ScriptBuilder.java b/src/com/fivium/scriptrunner2/ScriptBuilder.java index 31b0642..3e4493e 100644 --- a/src/com/fivium/scriptrunner2/ScriptBuilder.java +++ b/src/com/fivium/scriptrunner2/ScriptBuilder.java @@ -3,15 +3,19 @@ import com.fivium.scriptrunner2.builder.ManifestBuilder; import com.fivium.scriptrunner2.ex.ExFatalError; +import com.fivium.scriptrunner2.ex.ExManifest; import com.fivium.scriptrunner2.ex.ExManifestBuilder; import com.fivium.scriptrunner2.ex.ExParser; +import com.fivium.scriptrunner2.loader.MetadataLoader; import com.fivium.scriptrunner2.util.ArchiveUtil; import com.fivium.scriptrunner2.util.XFUtil; import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Collection; import java.util.Set; @@ -22,32 +26,48 @@ /** * Class for co-ordinating the creation of a promotion archive file. */ -public class ScriptBuilder { - +public class ScriptBuilder +implements FileResolver { + + /** Container for all command line options which were used to invoke ScriptRunner. */ + private final CommandLineWrapper mCommandLineWrapper; + + /** Source directory of the build. */ + private final File mSourceDirectory; + /** - * Generates a promotion archive by creating a manifest and zipping up all the manifest's implicated files into a single - * file. All options for this procedure should be specified on the command line. - * @param pOptionWrapper All command line options. - * @throws ExManifestBuilder If the manifest cannot be built. - * @throws ExParser If the manifest override or additional properties cannot be parsed. + * Creates a new ScriptBuilder. + * @param pCommandLineWrapper All command line options. * @throws ExFatalError If required command line options are missing or invalid. */ - public static void run(CommandLineWrapper pOptionWrapper) - throws ExManifestBuilder, ExParser { - + public ScriptBuilder(CommandLineWrapper pCommandLineWrapper) { + mCommandLineWrapper = pCommandLineWrapper; + //Determine source directory - - String lSourceDirectoryString = pOptionWrapper.getOption(CommandLineOption.BUILD); + + String lSourceDirectoryString = mCommandLineWrapper.getOption(CommandLineOption.BUILD); if(XFUtil.isNull(lSourceDirectoryString)){ throw new ExFatalError("-" + CommandLineOption.BUILD.getArgString() + " argument must be specified"); } Logger.logInfo("Source directory is " + lSourceDirectoryString); - - File lSourceDirectory = new File(lSourceDirectoryString); - + + mSourceDirectory = new File(lSourceDirectoryString); + } + + /** + * Generates a promotion archive by creating a manifest and zipping up all the manifest's implicated files into a single + * file. All options for this procedure should be specified on the command line. + * @throws ExManifest If validations of the manifest fail. + * @throws ExManifestBuilder If the manifest cannot be built. + * @throws ExParser If the manifest override or additional properties cannot be parsed. + * @throws ExFatalError If required command line options are missing or invalid. + */ + public void run() + throws ExManifest, ExManifestBuilder, ExParser { + //Determine promotion label - String lPromotionLabel = pOptionWrapper.getOption(CommandLineOption.PROMOTION_LABEL); + String lPromotionLabel = mCommandLineWrapper.getOption(CommandLineOption.PROMOTION_LABEL); if(XFUtil.isNull(lPromotionLabel)){ throw new ExFatalError("-" + CommandLineOption.PROMOTION_LABEL.getArgString() + " argument must be specified"); } @@ -55,7 +75,7 @@ public static void run(CommandLineWrapper pOptionWrapper) //Determine output location - String lOutputFileString = pOptionWrapper.getOption(CommandLineOption.OUTPUT_FILE_PATH); + String lOutputFileString = mCommandLineWrapper.getOption(CommandLineOption.OUTPUT_FILE_PATH); File lOutputFile; if(XFUtil.isNull(lOutputFileString)){ lOutputFile = new File(new File(System.getProperty("user.dir")), lPromotionLabel + ".zip"); @@ -66,7 +86,7 @@ public static void run(CommandLineWrapper pOptionWrapper) //Determine additional properties file location - String lAdditionalPropsPath = pOptionWrapper.getOption(CommandLineOption.ADDITIONAL_PROPERTIES); + String lAdditionalPropsPath = mCommandLineWrapper.getOption(CommandLineOption.ADDITIONAL_PROPERTIES); File lAdditionalPropsFile = null; if(!XFUtil.isNull(lAdditionalPropsPath)){ lAdditionalPropsFile = new File(lAdditionalPropsPath); @@ -75,7 +95,7 @@ public static void run(CommandLineWrapper pOptionWrapper) } } - File lManifestDestinationFile = new File(lSourceDirectory, ScriptRunner.MANIFEST_RELATIVE_FILE_PATH); + File lManifestDestinationFile = new File(mSourceDirectory, ScriptRunner.MANIFEST_RELATIVE_FILE_PATH); //Ensure the /ScriptRunner leg exists (it always should) lManifestDestinationFile.getParentFile().mkdirs(); PrintWriter lManifestDestinationWriter; @@ -87,7 +107,7 @@ public static void run(CommandLineWrapper pOptionWrapper) } //Construct the manifest file - ManifestBuilder lManifestBuilder = new ManifestBuilder(lSourceDirectory, lPromotionLabel); + ManifestBuilder lManifestBuilder = new ManifestBuilder(mSourceDirectory, lPromotionLabel); lManifestBuilder.buildManifest(lAdditionalPropsFile, lManifestDestinationWriter); //Check that all files in the source directory have been implicated @@ -98,26 +118,69 @@ public static void run(CommandLineWrapper pOptionWrapper) if(lFilePathsInBaseDirectory.size() > 0){ String warningMessage = lFilePathsInBaseDirectory.size() + " files found in source directory but not implicated by manifest builder rules:"; - if(pOptionWrapper.hasOption(CommandLineOption.NO_UNIMPLICATED_FILES)){ + if(mCommandLineWrapper.hasOption(CommandLineOption.NO_UNIMPLICATED_FILES)){ StringBuilder errorMessage = new StringBuilder(); errorMessage.append(warningMessage + "\n"); for (String lPath : lFilePathsInBaseDirectory) { errorMessage.append(lPath + "\n"); } throw new ExManifestBuilder(errorMessage.toString()); - } else { + } + else { Logger.logWarning(warningMessage); for (String lPath : lFilePathsInBaseDirectory) { Logger.logInfo(lPath); } } } + + // If the argument has been specified attempt to parse the manifest + // and then verify that all the Loader files exist in the source directory. + if(mCommandLineWrapper.hasOption(CommandLineOption.VERIFY_LOADERS)) { + //Parse the newly created manifest. + PromotionManifestParser lParser = new PromotionManifestParser(lManifestDestinationFile); + try { + lParser.parse(); + } + catch (FileNotFoundException e) { + throw new ExFatalError("Failed to parse manifest: file not found", e); + } + catch (IOException e) { + throw new ExFatalError("Failed to parse manifest: IOException", e); + } + catch (ExParser e) { + throw new ExFatalError("Failed to parse manifest: " + e.getMessage(), e); + } + catch (ExManifest e) { + throw new ExFatalError("Failed to load manifest: " + e.getMessage(), e); + } + + //Verify all loaders exist + ArrayList lUnverifiedLoaders = new ArrayList(); + for (MetadataLoader lLoader : lParser.getLoaderMap().values()) { + try { + this.resolveFile(lLoader.getLoaderFilePath()); + } + catch (FileNotFoundException e) { + lUnverifiedLoaders.add(lLoader.getName()); + } + } + + if (!lUnverifiedLoaders.isEmpty()) { + StringBuilder errorMessage = new StringBuilder(); + errorMessage.append(lUnverifiedLoaders.size() + " Loader files cannot be located:\n"); + for (String loaderName : lUnverifiedLoaders) { + errorMessage.append(loaderName + "\n"); + } + throw new ExManifest(errorMessage.toString()); + } + } //Get all non-ignored files to put in the zip lImplicatedManifestFilePaths = lManifestBuilder.allImplicatedFilePaths(false); //Get all files in the /ScriptRunner leg - Collection lScripRunnerFileList = FileUtils.listFiles(new File(lSourceDirectory, ScriptRunner.SCRIPTRUNNER_DIRECTORY_NAME), TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE); + Collection lScripRunnerFileList = FileUtils.listFiles(new File(mSourceDirectory, ScriptRunner.SCRIPTRUNNER_DIRECTORY_NAME), TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE); for(File lFile : lScripRunnerFileList){ lImplicatedManifestFilePaths.add(lManifestBuilder.relativeFilePath(lFile)); } @@ -125,8 +188,39 @@ public static void run(CommandLineWrapper pOptionWrapper) Logger.logAndEcho("Building promotion file " + lOutputFile.getAbsolutePath()); //Create the final archive in the output location - ArchiveUtil.createZip(lSourceDirectory, lImplicatedManifestFilePaths, lOutputFile); + ArchiveUtil.createZip(mSourceDirectory, lImplicatedManifestFilePaths, lOutputFile); } - + + /** + * Gets a file from this ScriptRunner's base directory. + * @param pPath A path to the desired file, relative to the source directory. + * @return The requested file. + * @throws FileNotFoundException If the file does not exist. + */ + public File resolveFile(String pPath) + throws FileNotFoundException { + File lFile = new File(mSourceDirectory, pPath); + + if(!lFile.exists()){ + throw new FileNotFoundException("Failed to locate file " + pPath + " in base directory"); + } + + return lFile; + } + + /** + * Gets the path of the given file relativised to the current source directory and normalised. + * @param pFile File to get path of. + * @return Relativised file path. + */ + public String relativeFilePath(File pFile){ + return ScriptRunner.normaliseFilePath(mSourceDirectory.toURI().relativize(pFile.toURI()).getPath()); + } + + @Override + public File getBaseDirectory() { + return mSourceDirectory; + } + }