From b9c111ce52761f474b936c0a842f75aab1ddb7f4 Mon Sep 17 00:00:00 2001 From: Mike Leonard Date: Tue, 8 Mar 2016 18:16:18 +1100 Subject: [PATCH 1/8] Add .gitignore to ignore build artefacts and IntelliJ config directories. --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..371fd70 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml +build-output/ +out/ From e95f5504578e4dc30c26e32441b40fc84f2764ca Mon Sep 17 00:00:00 2001 From: Mike Leonard Date: Tue, 8 Mar 2016 18:17:42 +1100 Subject: [PATCH 2/8] When writing logs also write a timestamp. --- src/com/fivium/scriptrunner2/Logger.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/com/fivium/scriptrunner2/Logger.java b/src/com/fivium/scriptrunner2/Logger.java index e689618..cb5afe7 100644 --- a/src/com/fivium/scriptrunner2/Logger.java +++ b/src/com/fivium/scriptrunner2/Logger.java @@ -38,6 +38,7 @@ public class Logger { private static final String LOG_FILE_NAME_PREFIX = "ScriptRunner-"; private static final String LOG_FILE_NAME_SUFFIX = ".log"; private static final DateFormat LOG_FILE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HHmmss"); + private static final DateFormat LOG_FILE_LOG_TIMESTAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); private static File gLogFile = null; @@ -125,9 +126,11 @@ public static void initialiseLogFile(File pLogDirectory) * @param pString Message. */ private static void log(String pString){ - //TODO log timestamps? for(Writer lWriter : gLogWriterList){ + String timeStamp = LOG_FILE_LOG_TIMESTAMP_FORMAT.format(new Date()); try { + lWriter.write("[" + timeStamp + "] "); + lWriter.write(pString); lWriter.write("\n"); lWriter.flush(); From b9bf0e8a9293829355877f5fb0313b674bb06939 Mon Sep 17 00:00:00 2001 From: Mike Leonard Date: Tue, 8 Mar 2016 18:49:24 +1100 Subject: [PATCH 3/8] Log timestamps when loggin exceptions. --- src/com/fivium/scriptrunner2/Logger.java | 2 +- src/com/fivium/scriptrunner2/Main.java | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/com/fivium/scriptrunner2/Logger.java b/src/com/fivium/scriptrunner2/Logger.java index cb5afe7..804bac6 100644 --- a/src/com/fivium/scriptrunner2/Logger.java +++ b/src/com/fivium/scriptrunner2/Logger.java @@ -38,7 +38,7 @@ public class Logger { private static final String LOG_FILE_NAME_PREFIX = "ScriptRunner-"; private static final String LOG_FILE_NAME_SUFFIX = ".log"; private static final DateFormat LOG_FILE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HHmmss"); - private static final DateFormat LOG_FILE_LOG_TIMESTAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + public static final DateFormat LOG_FILE_LOG_TIMESTAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); private static File gLogFile = null; diff --git a/src/com/fivium/scriptrunner2/Main.java b/src/com/fivium/scriptrunner2/Main.java index 0c6aa84..fc6f832 100644 --- a/src/com/fivium/scriptrunner2/Main.java +++ b/src/com/fivium/scriptrunner2/Main.java @@ -8,6 +8,9 @@ import java.io.File; import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.List; import org.apache.commons.cli.ParseException; @@ -110,7 +113,8 @@ else if(lCommandLineOptions.hasOption(CommandLineOption.PARSE_SCRIPTS)){ } } catch (Throwable th){ - System.err.println("Error encountered while running ScriptRunner (see log for details):"); + String timeStamp = Logger.LOG_FILE_LOG_TIMESTAMP_FORMAT.format(new Date()); + System.err.println("[" + timeStamp + "] Error encountered while running ScriptRunner (see log for details):"); System.err.println(th.getMessage()); if(!lCommandLineOptions.hasOption(CommandLineOption.RUN)){ //Error will already have been logged by runner; for all others log it now From f82f652670d61364c14c9b4bee4b9302d7562643 Mon Sep 17 00:00:00 2001 From: Mike Leonard Date: Tue, 8 Mar 2016 18:50:40 +1100 Subject: [PATCH 4/8] Add command line option "-nounimplicatedfiles". This causes ScriptRunner to raise an exception rather than log a warning when files are found in the source directory that are not implicated in the manifest. --- .../fivium/scriptrunner2/CommandLineOption.java | 3 ++- .../fivium/scriptrunner2/CommandLineWrapper.java | 2 ++ src/com/fivium/scriptrunner2/ScriptBuilder.java | 16 +++++++++++++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/com/fivium/scriptrunner2/CommandLineOption.java b/src/com/fivium/scriptrunner2/CommandLineOption.java index eadf22f..c123d46 100644 --- a/src/com/fivium/scriptrunner2/CommandLineOption.java +++ b/src/com/fivium/scriptrunner2/CommandLineOption.java @@ -25,7 +25,8 @@ public enum CommandLineOption { , DB_SYSDBA("sysdba") , OUTPUT_FILE_PATH("outfile") , PROMOTION_LABEL("label") - , ADDITIONAL_PROPERTIES("props"); + , ADDITIONAL_PROPERTIES("props") + , NO_UNIMPLICATED_FILES("nounimplicatedfiles"); private final String mArgString; diff --git a/src/com/fivium/scriptrunner2/CommandLineWrapper.java b/src/com/fivium/scriptrunner2/CommandLineWrapper.java index 747f52c..0d631f6 100644 --- a/src/com/fivium/scriptrunner2/CommandLineWrapper.java +++ b/src/com/fivium/scriptrunner2/CommandLineWrapper.java @@ -84,6 +84,8 @@ public class CommandLineWrapper { gCommandLineOptions.addOption(CommandLineOption.OUTPUT_FILE_PATH.getArgString(), true, "(Build only) File path where the output will be written to. Default is {CURRENT_DIR}/{PROMOTE_LABEL}.zip"); gCommandLineOptions.addOption(CommandLineOption.PROMOTION_LABEL.getArgString(), true, "(Build only) Promotion label for builder."); 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("help", false, "Prints help."); } diff --git a/src/com/fivium/scriptrunner2/ScriptBuilder.java b/src/com/fivium/scriptrunner2/ScriptBuilder.java index d5e8b4b..31b0642 100644 --- a/src/com/fivium/scriptrunner2/ScriptBuilder.java +++ b/src/com/fivium/scriptrunner2/ScriptBuilder.java @@ -97,9 +97,19 @@ public static void run(CommandLineWrapper pOptionWrapper) lFilePathsInBaseDirectory.removeAll(lImplicatedManifestFilePaths); if(lFilePathsInBaseDirectory.size() > 0){ - Logger.logWarning(lFilePathsInBaseDirectory.size() + " files found in source directory but not implicated by manifest builder rules:"); - for(String lPath : lFilePathsInBaseDirectory){ - Logger.logInfo(lPath); + String warningMessage = lFilePathsInBaseDirectory.size() + " files found in source directory but not implicated by manifest builder rules:"; + if(pOptionWrapper.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 { + Logger.logWarning(warningMessage); + for (String lPath : lFilePathsInBaseDirectory) { + Logger.logInfo(lPath); + } } } From b7a1c21f53c32e1c40367beeaa7b17daea1b0e96 Mon Sep 17 00:00:00 2001 From: Mike Leonard Date: Fri, 11 Mar 2016 11:59:30 +1100 Subject: [PATCH 5/8] Added -verifyloaders flag. This add a flag to the -build process to ensure that all the referenced Loaders exist at build time. --- .../scriptrunner2/CommandLineOption.java | 3 +- .../scriptrunner2/CommandLineWrapper.java | 1 + src/com/fivium/scriptrunner2/Main.java | 3 +- .../fivium/scriptrunner2/ScriptBuilder.java | 130 ++++++++++++++---- 4 files changed, 111 insertions(+), 26 deletions(-) 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..23edf09 100644 --- a/src/com/fivium/scriptrunner2/ScriptBuilder.java +++ b/src/com/fivium/scriptrunner2/ScriptBuilder.java @@ -3,13 +3,16 @@ 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.Collection; @@ -22,32 +25,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 +74,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 +85,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 +94,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 +106,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,7 +117,7 @@ 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) { @@ -112,12 +131,44 @@ public static void run(CommandLineWrapper pOptionWrapper) } } } + + // 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 + for (MetadataLoader lLoader : lParser.getLoaderMap().values()) { + try { + this.resolveFile(lLoader.getLoaderFilePath()); + } + catch (FileNotFoundException e) { + throw new ExManifest("Loader file for loader " + lLoader.getName() + " cannot be located", e); + } + } + } //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 +176,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 base 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 base 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; + } + } From cfd809995e1431d71ace30cf34506ce14b82caa2 Mon Sep 17 00:00:00 2001 From: Mike Leonard Date: Fri, 11 Mar 2016 12:08:29 +1100 Subject: [PATCH 6/8] Comment changes to make it more obvious that in the builder the files base directory is the source directory. --- src/com/fivium/scriptrunner2/ScriptBuilder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/fivium/scriptrunner2/ScriptBuilder.java b/src/com/fivium/scriptrunner2/ScriptBuilder.java index 23edf09..c3260e0 100644 --- a/src/com/fivium/scriptrunner2/ScriptBuilder.java +++ b/src/com/fivium/scriptrunner2/ScriptBuilder.java @@ -182,7 +182,7 @@ public void run() /** * Gets a file from this ScriptRunner's base directory. - * @param pPath A path to the desired file, relative to the 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. */ @@ -198,7 +198,7 @@ public File resolveFile(String pPath) } /** - * Gets the path of the given file relativised to the current base directory and normalised. + * 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. */ From b4d652bd9da2e466a7c80c32e04c4bd7a37163ee Mon Sep 17 00:00:00 2001 From: Mike Leonard Date: Fri, 11 Mar 2016 12:13:41 +1100 Subject: [PATCH 7/8] Moved else to new line as per Nicks code review in #10. --- src/com/fivium/scriptrunner2/ScriptBuilder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/fivium/scriptrunner2/ScriptBuilder.java b/src/com/fivium/scriptrunner2/ScriptBuilder.java index c3260e0..10437bc 100644 --- a/src/com/fivium/scriptrunner2/ScriptBuilder.java +++ b/src/com/fivium/scriptrunner2/ScriptBuilder.java @@ -124,7 +124,8 @@ public void run() errorMessage.append(lPath + "\n"); } throw new ExManifestBuilder(errorMessage.toString()); - } else { + } + else { Logger.logWarning(warningMessage); for (String lPath : lFilePathsInBaseDirectory) { Logger.logInfo(lPath); From 695ab58e9d84723c7773a74354ee14edf7f7c7eb Mon Sep 17 00:00:00 2001 From: Mike Leonard Date: Fri, 11 Mar 2016 12:50:45 +1100 Subject: [PATCH 8/8] Log the names of all the loaders that couldn't be found rather than just the first one found. --- src/com/fivium/scriptrunner2/ScriptBuilder.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/com/fivium/scriptrunner2/ScriptBuilder.java b/src/com/fivium/scriptrunner2/ScriptBuilder.java index 10437bc..3e4493e 100644 --- a/src/com/fivium/scriptrunner2/ScriptBuilder.java +++ b/src/com/fivium/scriptrunner2/ScriptBuilder.java @@ -15,6 +15,7 @@ import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Collection; import java.util.Set; @@ -155,14 +156,24 @@ public void run() } //Verify all loaders exist + ArrayList lUnverifiedLoaders = new ArrayList(); for (MetadataLoader lLoader : lParser.getLoaderMap().values()) { try { this.resolveFile(lLoader.getLoaderFilePath()); } catch (FileNotFoundException e) { - throw new ExManifest("Loader file for loader " + lLoader.getName() + " cannot be located", 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