mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
FIXED : ALF-12777: MMT should not install AMPs which override pre-existing files in the war file, unless -force is provided
The MMT is moving toward more of a validation phase (checks things, calculate changes) then an execution phase (makes the changes). git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@33880 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -26,6 +26,7 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
@@ -196,7 +197,7 @@ public class ModuleManagementTool implements LogOutput
|
|||||||
* @param warFileLocation the location of the WAR file into which the AMP file is 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
|
* @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.
|
* 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
|
* @param forceInstall indicates whether the installed files will be replaces regardless of the currently installed
|
||||||
* version of the AMP. Generally used during development of the AMP.
|
* 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
|
* @param backupWAR indicates whether we should backup the war we are modifying or not
|
||||||
*/
|
*/
|
||||||
@@ -240,105 +241,29 @@ public class ModuleManagementTool implements LogOutput
|
|||||||
// Try to find an installed module by the ID
|
// Try to find an installed module by the ID
|
||||||
ModuleDetails installedModuleDetails = warHelper.getModuleDetailsOrAlias(theWar, installingModuleDetails);
|
ModuleDetails installedModuleDetails = warHelper.getModuleDetailsOrAlias(theWar, installingModuleDetails);
|
||||||
|
|
||||||
// Now clean up the old instance
|
uninstallIfNecessary(warFileLocation, installedModuleDetails, preview, forceInstall, installingVersion);
|
||||||
if (installedModuleDetails != null)
|
|
||||||
{
|
|
||||||
String installedId = installedModuleDetails.getId();
|
|
||||||
VersionNumber installedVersion = installedModuleDetails.getVersion();
|
|
||||||
|
|
||||||
int compareValue = installedVersion.compareTo(installingVersion);
|
|
||||||
if (compareValue > 0)
|
|
||||||
{
|
|
||||||
// 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. "+
|
|
||||||
"You could force the installation by passing the -force option.",false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.",false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compareValue == 0)
|
|
||||||
{
|
|
||||||
// Trying to install the same extension version again
|
|
||||||
outputMessage("WARNING: This version of this module is already installed in the WAR..upgrading.",false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forceInstall == true || compareValue <= 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
// Trying to update the extension, old files need to cleaned before we proceed
|
|
||||||
outputMessage("Clearing out files relating to version '" + installedVersion + "' of module '" + installedId + "'",false);
|
|
||||||
uninstallModule(installedId, warFileLocation, preview, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 + "'");
|
outputMessage("Adding files relating to version '" + installingVersion + "' of module '" + installingId + "'");
|
||||||
InstalledFiles installedFiles = new InstalledFiles(warFileLocation, installingId);
|
InstalledFiles installedFiles = new InstalledFiles(warFileLocation, installingId);
|
||||||
for (Map.Entry<Object, Object> entry : fileMappingProperties.entrySet())
|
|
||||||
{
|
Properties directoryChanges = calculateChanges(ampFileLocation, warFileLocation, preview, forceInstall, installedFiles);
|
||||||
// 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 + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
mappingSource = mappingSource.trim(); //trim whitespace
|
|
||||||
mappingTarget = mappingTarget.trim(); //trim whitespace
|
|
||||||
|
|
||||||
// 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)
|
if (preview == false)
|
||||||
{
|
{
|
||||||
|
//Now actually do the changes
|
||||||
|
if (directoryChanges != null && directoryChanges.size() > 0)
|
||||||
|
{
|
||||||
|
for (Entry<Object, Object> entry : directoryChanges.entrySet())
|
||||||
|
{
|
||||||
|
|
||||||
|
File destination = new File((String) entry.getValue(), DETECTOR_AMP_AND_WAR);
|
||||||
|
File source = new File((String) entry.getKey(), DETECTOR_AMP_AND_WAR);
|
||||||
|
//Do the bulk copy since this is quicker than copying files one by one
|
||||||
|
//The changes aren't actuall "committed" until the File.update() is called (below)
|
||||||
|
destination.copyAllFrom(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Save the installed file list
|
// Save the installed file list
|
||||||
installedFiles.save();
|
installedFiles.save();
|
||||||
|
|
||||||
@@ -375,6 +300,109 @@ public class ModuleManagementTool implements LogOutput
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void uninstallIfNecessary(String warFileLocation, ModuleDetails installedModuleDetails, boolean preview,
|
||||||
|
boolean forceInstall, VersionNumber installingVersion)
|
||||||
|
{
|
||||||
|
// Now clean up the old instance
|
||||||
|
if (installedModuleDetails != null)
|
||||||
|
{
|
||||||
|
String installedId = installedModuleDetails.getId();
|
||||||
|
VersionNumber installedVersion = installedModuleDetails.getVersion();
|
||||||
|
|
||||||
|
int compareValue = installedVersion.compareTo(installingVersion);
|
||||||
|
if (compareValue > 0)
|
||||||
|
{
|
||||||
|
// 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. "+
|
||||||
|
"You could force the installation by passing the -force option.",false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.",false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compareValue == 0)
|
||||||
|
{
|
||||||
|
// Trying to install the same extension version again
|
||||||
|
outputMessage("WARNING: This version of this module is already installed in the WAR..upgrading.",false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forceInstall == true || compareValue <= 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Trying to update the extension, old files need to cleaned before we proceed
|
||||||
|
outputMessage("Clearing out files relating to version '" + installedVersion + "' of module '" + installedId + "'",false);
|
||||||
|
uninstallModule(installedId, warFileLocation, preview, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private Properties calculateChanges(String ampFileLocation, String warFileLocation, boolean preview,
|
||||||
|
boolean forceInstall, InstalledFiles installedFiles) throws IOException
|
||||||
|
{
|
||||||
|
Properties dirChanges = new Properties();
|
||||||
|
|
||||||
|
// 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
|
||||||
|
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 + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
mappingSource = mappingSource.trim(); //trim whitespace
|
||||||
|
mappingTarget = mappingTarget.trim(); //trim whitespace
|
||||||
|
|
||||||
|
// Run throught the files one by one figuring out what we are going to do during the copy
|
||||||
|
calculateCopyToWar(ampFileLocation, warFileLocation, mappingSource, mappingTarget, installedFiles, preview, forceInstall);
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
// Add to the list of directory changes so we can implement the changes later.
|
||||||
|
String sourceDir = ampFileLocation + mappingSource;
|
||||||
|
String destinationDir = warFileLocation + mappingTarget;
|
||||||
|
dirChanges.put(sourceDir, destinationDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirChanges;
|
||||||
|
}
|
||||||
|
|
||||||
private void backupWar(String warFileLocation, boolean backupWAR)
|
private void backupWar(String warFileLocation, boolean backupWAR)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -511,9 +539,11 @@ public class ModuleManagementTool implements LogOutput
|
|||||||
* @param destinationDir the directory in the WAR to copy to. It must start with "/".
|
* @param destinationDir the directory in the WAR to copy to. It must start with "/".
|
||||||
* @param installedFiles a list of the currently installed files
|
* @param installedFiles a list of the currently installed files
|
||||||
* @param preview indicates whether this is a preview install or not
|
* @param preview indicates whether this is a preview install or not
|
||||||
|
* @param forceInstall indicates whether the installed files will be replaces regardless of the currently installed
|
||||||
|
* version of the AMP.
|
||||||
* @throws IOException throws any IOExpceptions thar are raised
|
* @throws IOException throws any IOExpceptions thar are raised
|
||||||
*/
|
*/
|
||||||
private void copyToWar(String ampFileLocation, String warFileLocation, String sourceDir, String destinationDir, InstalledFiles installedFiles, boolean preview)
|
private void calculateCopyToWar(String ampFileLocation, String warFileLocation, String sourceDir, String destinationDir, InstalledFiles installedFiles, boolean preview, boolean forceInstall)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
if (sourceDir.length() == 0 || !sourceDir.startsWith("/"))
|
if (sourceDir.length() == 0 || !sourceDir.startsWith("/"))
|
||||||
@@ -555,12 +585,22 @@ public class ModuleManagementTool implements LogOutput
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Backup file about to be updated
|
if (forceInstall)
|
||||||
backupLocation = BACKUP_DIR + "/" + generateGuid() + ".bin";
|
|
||||||
if (preview == false)
|
|
||||||
{
|
{
|
||||||
File backupFile = new File(warFileLocation + backupLocation, DETECTOR_AMP_AND_WAR);
|
// Backup file about to be updated
|
||||||
backupFile.copyFrom(destinationChild);
|
backupLocation = BACKUP_DIR + "/" + generateGuid() + ".bin";
|
||||||
|
if (preview == false)
|
||||||
|
{
|
||||||
|
File backupFile = new File(warFileLocation + backupLocation, DETECTOR_AMP_AND_WAR);
|
||||||
|
backupFile.copyFrom(destinationChild);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Not a forced install, there is an existing file in the war, lets rollback the transaction,
|
||||||
|
//throw an error and explain the problem.
|
||||||
|
// File.
|
||||||
|
// ZipController zipController = ZipController.getInstance(warFile);
|
||||||
|
// zipController.reset();
|
||||||
|
throw new ModuleManagementToolException("ERROR: The amp will overwrite an existing file in the war '" + destinationDir + "/" + sourceChild.getName() + "'. Execution halted. By specifying -force , you can force installation of AMP regardless of the current war state.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,8 +623,8 @@ public class ModuleManagementTool implements LogOutput
|
|||||||
mkdir = true;
|
mkdir = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
copyToWar(ampFileLocation, warFileLocation, sourceDir + "/" + sourceChild.getName(),
|
calculateCopyToWar(ampFileLocation, warFileLocation, sourceDir + "/" + sourceChild.getName(),
|
||||||
destinationDir + "/" + sourceChild.getName(), installedFiles, preview);
|
destinationDir + "/" + sourceChild.getName(), installedFiles, preview, forceInstall);
|
||||||
if (mkdir == true)
|
if (mkdir == true)
|
||||||
{
|
{
|
||||||
installedFiles.addMkdir(destinationDir + "/" + sourceChild.getName());
|
installedFiles.addMkdir(destinationDir + "/" + sourceChild.getName());
|
||||||
|
@@ -81,19 +81,9 @@ public class ModuleManagementToolTest extends TestCase
|
|||||||
InstalledFiles installed0 = new InstalledFiles(warLocation, "test");
|
InstalledFiles installed0 = new InstalledFiles(warLocation, "test");
|
||||||
installed0.load();
|
installed0.load();
|
||||||
assertNotNull(installed0);
|
assertNotNull(installed0);
|
||||||
assertEquals(8, installed0.getAdds().size());
|
assertEquals(9, installed0.getAdds().size());
|
||||||
assertEquals(1, installed0.getMkdirs().size());
|
assertEquals(1, installed0.getMkdirs().size());
|
||||||
assertEquals(1, installed0.getUpdates().size());
|
|
||||||
String backup = null;
|
|
||||||
String orig = null;
|
|
||||||
for (Map.Entry<String, String> update : installed0.getUpdates().entrySet())
|
|
||||||
{
|
|
||||||
checkContentsOfFile(warLocation + update.getKey(), "VERSIONONE");
|
|
||||||
checkContentsOfFile(warLocation + update.getValue(), "ORIGIONAL");
|
|
||||||
backup = update.getValue();
|
|
||||||
orig = update.getKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try and install same version
|
// Try and install same version
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -129,7 +119,6 @@ public class ModuleManagementToolTest extends TestCase
|
|||||||
files3.add("/scripts/test.js");
|
files3.add("/scripts/test.js");
|
||||||
files3.add("/jsp/test.jsp");
|
files3.add("/jsp/test.jsp");
|
||||||
files3.add("/extra.txt");
|
files3.add("/extra.txt");
|
||||||
files3.add(backup);
|
|
||||||
checkForFileNonExistance(warLocation, files3);
|
checkForFileNonExistance(warLocation, files3);
|
||||||
|
|
||||||
// Check the intstalled files
|
// Check the intstalled files
|
||||||
@@ -139,9 +128,6 @@ public class ModuleManagementToolTest extends TestCase
|
|||||||
assertEquals(8, installed1.getAdds().size());
|
assertEquals(8, installed1.getAdds().size());
|
||||||
assertEquals(1, installed1.getMkdirs().size());
|
assertEquals(1, installed1.getMkdirs().size());
|
||||||
assertEquals(0, installed1.getUpdates().size());
|
assertEquals(0, installed1.getUpdates().size());
|
||||||
|
|
||||||
// Ensure the file has been reverted as it isnt updated in the v2.0
|
|
||||||
checkContentsOfFile(warLocation + orig, "ORIGIONAL");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try and install an earlier version over a later version
|
* Try and install an earlier version over a later version
|
||||||
@@ -269,6 +255,28 @@ public class ModuleManagementToolTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testExistingFilesInWar() throws Exception
|
||||||
|
{
|
||||||
|
manager.setVerbose(true);
|
||||||
|
|
||||||
|
String warLocation = getFileLocation(".war", "module/test.war");
|
||||||
|
String ampLocation = getFileLocation(".amp", "module/test_v4.amp");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.manager.installModule(ampLocation, warLocation, false, false, true);
|
||||||
|
}
|
||||||
|
catch(ModuleManagementToolException exception)
|
||||||
|
{
|
||||||
|
assertTrue(exception.getMessage().contains("will overwrite an existing file in the war"));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.manager.installModule(ampLocation, warLocation, false, true, true); //Now force it
|
||||||
|
checkContentsOfFile(warLocation + "/jsp/relogin.jsp", "VERSIONONE");
|
||||||
|
checkContentsOfFile(warLocation + "/css/main.css", "p{margin-bottom:1em;}");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void testWhiteSpaceInCustomMapping()
|
public void testWhiteSpaceInCustomMapping()
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
|
Binary file not shown.
BIN
source/test-resources/module/test_v4.amp
Normal file
BIN
source/test-resources/module/test_v4.amp
Normal file
Binary file not shown.
Reference in New Issue
Block a user