/** * Cleans the WAR file of all files relating to the currently installed version of the the AMP. * * @param warFileLocatio the war file location * @param moduleId the module id * @param preview indicates whether this is a preview installation */ private void cleanWAR(String warFileLocation, String moduleId, boolean preview) { InstalledFiles installedFiles = new InstalledFiles(warFileLocation, moduleId); installedFiles.load(); for (String add : installedFiles.getAdds()) { // Remove file removeFile(warFileLocation, add, preview); } for (String mkdir : installedFiles.getMkdirs()) { // Remove folder removeFile(warFileLocation, mkdir, preview); } for (Map.Entry<String, String> update : installedFiles.getUpdates().entrySet()) { if (preview == false) { // Recover updated file and delete backups File modified = new File(warFileLocation + update.getKey(), DETECTOR_AMP_AND_WAR); File backup = new File(warFileLocation + update.getValue(), DETECTOR_AMP_AND_WAR); modified.copyFrom(backup); backup.delete(); } outputMessage( "Recovering file '" + update.getKey() + "' from backup '" + update.getValue() + "'", true); } // Now remove the installed files list String installedFilesPathInWar = installedFiles.getFilePathInWar(); removeFile(warFileLocation, installedFilesPathInWar, preview); // Remove the module properties String modulePropertiesFileLocationInWar = ModuleDetailsHelper.getModulePropertiesFilePathInWar(moduleId); removeFile(warFileLocation, modulePropertiesFileLocationInWar, preview); }
/** * Lists all the currently installed modules in the WAR * * @param warLocation the war location */ public void listModules(String warLocation) { ModuleDetails moduleDetails = null; boolean previous = this.verbose; this.verbose = true; try { File moduleDir = new File(warLocation + MODULE_DIR, DETECTOR_AMP_AND_WAR); if (moduleDir.exists() == false) { outputMessage("No modules are installed in this WAR file"); } java.io.File[] dirs = moduleDir.listFiles(); if (dirs != null && dirs.length != 0) { for (java.io.File dir : dirs) { if (dir.isDirectory() == true) { File moduleProperties = new File(dir.getPath() + "/module.properties", DETECTOR_AMP_AND_WAR); if (moduleProperties.exists() == true) { try { InputStream is = new FileInputStream(moduleProperties); moduleDetails = ModuleDetailsHelper.createModuleDetailsFromPropertiesStream(is); } catch (IOException exception) { throw new ModuleManagementToolException( "Unable to open module properties file '" + moduleProperties.getPath() + "'", exception); } outputMessage( "Module '" + moduleDetails.getId() + "' installed in '" + warLocation + "'"); outputMessage(" Title: " + moduleDetails.getTitle(), true); outputMessage(" Version: " + moduleDetails.getVersion(), true); outputMessage(" Install Date: " + moduleDetails.getInstallDate(), true); outputMessage(" Description: " + moduleDetails.getDescription(), true); } } } } else { outputMessage("No modules are installed in this WAR file"); } } finally { this.verbose = previous; } }
@Test public void testCheckCompatibleEditionUsingManifest() throws IOException { Properties props = dummyModuleProperties(); ModuleDetails installingModuleDetails = new ModuleDetailsImpl(props); TFile theWar = getFile(".war", "module/share-3.4.11.war"); // enterprise edition // Test for no edition specified this.checkCompatibleEditionUsingManifest( theWar, installingModuleDetails); // does not throw exception // Test for invalid edition props.setProperty(ModuleDetails.PROP_EDITIONS, "CommuniT"); installingModuleDetails = new ModuleDetailsImpl(props); try { this.checkCompatibleEditionUsingManifest(theWar, installingModuleDetails); fail(); // should never get here } catch (ModuleManagementToolException exception) { assertTrue( exception .getMessage() .endsWith("can only be installed in one of the following editions[CommuniT]")); } props.setProperty(ModuleDetails.PROP_EDITIONS, ("Enterprise")); // should ignore case installingModuleDetails = new ModuleDetailsImpl(props); this.checkCompatibleEditionUsingManifest( theWar, installingModuleDetails); // does not throw exception props.setProperty(ModuleDetails.PROP_EDITIONS, ("Community")); // should ignore case installingModuleDetails = new ModuleDetailsImpl(props); try { this.checkCompatibleEditionUsingManifest(theWar, installingModuleDetails); fail(); // should never get here } catch (ModuleManagementToolException exception) { assertTrue( exception .getMessage() .endsWith("can only be installed in one of the following editions[Community]")); } theWar = getFile(".war", "module/share-4.2.a.war"); this.checkCompatibleEditionUsingManifest(theWar, installingModuleDetails); String propertiesLocation = getFile(".amp", "module/test_v5.amp") + "/module.properties"; installingModuleDetails = ModuleDetailsHelper.createModuleDetailsFromPropertyLocation(propertiesLocation); try { this.checkCompatibleEdition(theWar, installingModuleDetails); fail(); // should never get here } catch (ModuleManagementToolException exception) { assertTrue( exception .getMessage() .endsWith("can only be installed in one of the following editions[Enterprise]")); } theWar = getFile(".war", "module/share-3.4.11.war"); this.checkCompatibleEdition(theWar, installingModuleDetails); // should succeed try { theWar = getFile(".war", "module/share-4.2.a.war"); this.checkCompatibleEdition(theWar, installingModuleDetails); fail(); // should never get here } catch (ModuleManagementToolException exception) { assertTrue( exception .getMessage() .endsWith("can only be installed in one of the following editions[Enterprise]")); } }
/** * Installs a given AMP file into a given WAR file. * * @param ampFileLocation the location of the AMP file to be installed * @param warFileLocation the location of the WAR file into which the AMP file is to be installed. * @param preview indicates whether this should be a preview install. This means that the process * of installation will be followed and reported, but the WAR file will not be modified. * @param forceInstall indicates whether the installed files will be replaces reguarless of the * currently installed version of the AMP. Generally used during development of the AMP. * @param backupWAR indicates whether we should backup the war we are modifying or not */ public void installModule( String ampFileLocation, String warFileLocation, boolean preview, boolean forceInstall, boolean backupWAR) { try { outputMessage("Installing AMP '" + ampFileLocation + "' into WAR '" + warFileLocation + "'"); if (preview == false) { // Make sure the module and backup directory exisits in the WAR file File moduleDir = new File(warFileLocation + MODULE_DIR, DETECTOR_AMP_AND_WAR); if (moduleDir.exists() == false) { moduleDir.mkdir(); } File backUpDir = new File(warFileLocation + BACKUP_DIR, DETECTOR_AMP_AND_WAR); if (backUpDir.exists() == false) { backUpDir.mkdir(); } // Make a backup of the war we are going to modify if (backupWAR == true) { java.io.File warFile = new java.io.File(warFileLocation); if (warFile.exists() == false) { throw new ModuleManagementToolException( "The war file '" + warFileLocation + "' does not exist."); } String backupLocation = warFileLocation + "-" + System.currentTimeMillis() + ".bak"; java.io.File backup = new java.io.File(backupLocation); copyFile(warFile, backup); outputMessage("WAR has been backed up to '" + backupLocation + "'"); } } // Get the details of the installing module String propertiesLocation = ampFileLocation + "/module.properties"; ModuleDetails installingModuleDetails = ModuleDetailsHelper.createModuleDetailsFromPropertyLocation(propertiesLocation); if (installingModuleDetails == null) { throw new ModuleManagementToolException( "No module.properties file has been found in the installing .amp file '" + ampFileLocation + "'"); } String installingId = installingModuleDetails.getId(); VersionNumber installingVersion = installingModuleDetails.getVersion(); // Check that the target war has the necessary dependencies for this install List<ModuleDependency> installingModuleDependencies = installingModuleDetails.getDependencies(); List<ModuleDependency> missingDependencies = new ArrayList<ModuleDependency>(0); for (ModuleDependency dependency : installingModuleDependencies) { String dependencyId = dependency.getDependencyId(); ModuleDetails dependencyModuleDetails = ModuleDetailsHelper.createModuleDetailsFromWarAndId(warFileLocation, dependencyId); // Check the dependency. The API specifies that a null returns false, so no null check is // required if (!dependency.isValidDependency(dependencyModuleDetails)) { missingDependencies.add(dependency); continue; } } if (missingDependencies.size() > 0) { throw new ModuleManagementToolException( "The following modules must first be installed: " + missingDependencies); } // Try to find an installed module by the ID ModuleDetails installedModuleDetails = ModuleDetailsHelper.createModuleDetailsFromWarAndId(warFileLocation, installingId); if (installedModuleDetails == null) { // It might be there as one of the aliases List<String> installingAliases = installingModuleDetails.getAliases(); for (String installingAlias : installingAliases) { ModuleDetails installedAliasModuleDetails = ModuleDetailsHelper.createModuleDetailsFromWarAndId(warFileLocation, installingAlias); if (installedAliasModuleDetails == null) { // There is nothing by that alias continue; } // We found an alias and will treat it as the same module installedModuleDetails = installedAliasModuleDetails; outputMessage( "Module '" + installingAlias + "' is installed and is an alias of '" + installingId + "'"); break; } } // Now clean up the old instance if (installedModuleDetails != null) { String installedId = installedModuleDetails.getId(); VersionNumber installedVersion = installedModuleDetails.getVersion(); int compareValue = installedVersion.compareTo(installingVersion); if (forceInstall == true || compareValue == -1) { if (forceInstall == true) { // Warn of forced install outputMessage( "WARNING: The installation of this module is being forced. All files will be removed and replaced regardless of exiting versions present."); } // Trying to update the extension, old files need to cleaned before we proceed outputMessage( "Clearing out files relating to version '" + installedVersion + "' of module '" + installedId + "'"); cleanWAR(warFileLocation, installedId, preview); } else if (compareValue == 0) { // Trying to install the same extension version again outputMessage( "WARNING: This version of this module is already installed in the WAR. Installation skipped."); return; } else if (compareValue == 1) { // Trying to install an earlier version of the extension outputMessage( "WARNING: A later version of this module is already installed in the WAR. Installation skipped."); return; } } // Check if a custom mapping file has been defined Properties fileMappingProperties = null; Properties customFileMappingProperties = getCustomFileMappings(ampFileLocation); if (customFileMappingProperties == null) { fileMappingProperties = defaultFileMappingProperties; } else { fileMappingProperties = new Properties(); // A custom mapping file was present. Check if it must inherit the default mappings. String inheritDefaultStr = customFileMappingProperties.getProperty(PROP_INHERIT_DEFAULT, "true"); if (inheritDefaultStr.equalsIgnoreCase("true")) { fileMappingProperties.putAll(defaultFileMappingProperties); } fileMappingProperties.putAll(customFileMappingProperties); fileMappingProperties.remove(PROP_INHERIT_DEFAULT); } // Copy the files from the AMP file into the WAR file outputMessage( "Adding files relating to version '" + installingVersion + "' of module '" + installingId + "'"); InstalledFiles installedFiles = new InstalledFiles(warFileLocation, installingId); for (Map.Entry<Object, Object> entry : fileMappingProperties.entrySet()) { // The file mappings are expected to start with "/" String mappingSource = (String) entry.getKey(); if (mappingSource.length() == 0 || !mappingSource.startsWith("/")) { throw new AlfrescoRuntimeException( "File mapping sources must start with '/', but was: " + mappingSource); } String mappingTarget = (String) entry.getValue(); if (mappingTarget.length() == 0 || !mappingTarget.startsWith("/")) { throw new AlfrescoRuntimeException( "File mapping targets must start with '/' but was '" + mappingTarget + "'"); } // Run throught the files one by one figuring out what we are going to do during the copy copyToWar( ampFileLocation, warFileLocation, mappingSource, mappingTarget, installedFiles, preview); if (preview == false) { // Get a reference to the source folder (if it isn't present don't do anything) File source = new File(ampFileLocation + "/" + mappingSource, DETECTOR_AMP_AND_WAR); if (source != null && source.list() != null) { // Get a reference to the destination folder File destination = new File(warFileLocation + "/" + mappingTarget, DETECTOR_AMP_AND_WAR); if (destination == null) { throw new ModuleManagementToolException( "The destination folder '" + mappingTarget + "' as specified in mapping properties does not exist in the war"); } // Do the bulk copy since this is quicker than copying files one by one destination.copyAllFrom(source); } } } if (preview == false) { // Save the installed file list installedFiles.save(); // Update the installed module details installingModuleDetails.setInstallState(ModuleInstallState.INSTALLED); installingModuleDetails.setInstallDate(new Date()); ModuleDetailsHelper.saveModuleDetails(warFileLocation, installingModuleDetails); // Update the zip files File.update(); // Set the modified date java.io.File warFile = new java.io.File(warFileLocation); if (warFile.exists()) { warFile.setLastModified(System.currentTimeMillis()); } } } catch (ZipWarningException ignore) { // Only instances of the class ZipWarningException exist in the chain of // exceptions. We choose to ignore this. } catch (ZipControllerException exception) { // At least one exception occured which is not just a ZipWarningException. // This is a severe situation that needs to be handled. throw new ModuleManagementToolException( "A Zip error was encountered during deployment of the AEP into the WAR", exception); } catch (IOException exception) { throw new ModuleManagementToolException( "An IO error was encountered during deployment of the AEP into the WAR", exception); } }