Bump reflections from 0.9.12 to 0.10.2 (#776)

This commit is contained in:
dependabot[bot]
2022-02-03 10:51:48 +00:00
committed by GitHub
parent 9bcf7eed9a
commit 44a8acddd0
35 changed files with 281 additions and 272 deletions

View File

@@ -31,6 +31,11 @@
<artifactId>maven-artifact</artifactId>
<version>${dependency.maven-artifact.version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@@ -0,0 +1,263 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.module.tool;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileOutputStream;
import de.schlichtherle.truezip.file.TFileReader;
/**
* Details of the files installed during a module installation into a WAR
*
* @author Roy Wetherall
*/
public class InstalledFiles
{
/** Modification types */
private static final String MOD_ADD_FILE = "add";
private static final String MOD_UPDATE_FILE = "update";
private static final String MOD_MK_DIR = "mkdir";
/** Delimieter used in the file */
private static final String DELIMITER = "|";
/** War location **/
private String warLocation;
/** Module id **/
private String moduleId;
/** Lists containing the modifications made */
private List<String> adds = new ArrayList<String>();
private Map<String, String> updates = new HashMap<String, String>();
private List<String> mkdirs = new ArrayList<String>();
/**
* Constructor
*
* @param warLocation the war location
* @param moduleId the module id
*/
public InstalledFiles(String warLocation, String moduleId)
{
this.warLocation = warLocation;
this.moduleId = moduleId;
}
/**
* Loads the exisiting information about the installed files from the WAR
*/
public void load()
{
TFile file = new TFile(getFileLocation());
if (file.exists() == true)
{
try
{
BufferedReader reader = new BufferedReader(new TFileReader(file));
try
{
String line = reader.readLine();
while (line != null)
{
String[] modification = line.split("\\" + DELIMITER);
String mod = modification[0];
String location = modification[1];
if (mod.equals(MOD_ADD_FILE) == true)
{
this.adds.add(location);
}
else if (mod.equals(MOD_MK_DIR) == true)
{
this.mkdirs.add(location);
}
else if (mod.equals(MOD_UPDATE_FILE) == true)
{
this.updates.put(location, modification[2]);
}
line = reader.readLine();
}
}
finally
{
reader.close();
}
}
catch(FileNotFoundException exception)
{
throw new ModuleManagementToolException("The module file install file '" + getFileLocation() + "' does not exist", exception);
}
catch(IOException exception)
{
throw new ModuleManagementToolException("Error whilst reading file '" + getFileLocation(), exception);
}
}
else
{
throw new ModuleManagementToolException("Invalid module. The installation file does not exist for module: "+moduleId);
}
}
/**
* Saves the current modification details into the WAR
*/
public void save()
{
try
{
TFile file = new TFile(getFileLocation());
if (file.exists() == false)
{
file.createNewFile();
}
TFileOutputStream os = new TFileOutputStream(file);
try
{
for (String add : this.adds)
{
String output = MOD_ADD_FILE + DELIMITER + add + "\n";
os.write(output.getBytes());
}
for (Map.Entry<String, String> update : this.updates.entrySet())
{
String output = MOD_UPDATE_FILE + DELIMITER + update.getKey() + DELIMITER + update.getValue() + "\n";
os.write(output.getBytes());
}
for (String mkdir : this.mkdirs)
{
String output = MOD_MK_DIR + DELIMITER + mkdir + "\n";
os.write(output.getBytes());
}
}
finally
{
os.close();
}
}
catch(IOException exception)
{
throw new ModuleManagementToolException("Error whilst saving modifications file.", exception);
}
}
/**
* Returns the location of the modifications file based on the module id
*
* @return the file location
*/
public String getFileLocation()
{
return this.warLocation + getFilePathInWar();
}
/**
* @return Returns the path of the install file within the WAR
*/
public String getFilePathInWar()
{
return WarHelper.MODULE_NAMESPACE_DIR + "/" + this.moduleId + "/modifications.install";
}
/**
* Get all the added files
*
* @return list of files added to war
*/
public List<String> getAdds()
{
return adds;
}
/**
* Get all the updated files, key is the file that has been updated and the value is the
* location of the backup made before modification took place.
*
* @return map of file locaiton and backup
*/
public Map<String, String> getUpdates()
{
return updates;
}
/**
* Gets a list of the dirs added during install
*
* @return list of directories added
*/
public List<String> getMkdirs()
{
return mkdirs;
}
/**
* Add a file addition
*
* @param location the file added
*/
public void addAdd(String location)
{
this.adds.add(location);
}
/**
* Add a file update
*
* @param location the file updated
* @param backup the backup location
*/
public void addUpdate(String location, String backup)
{
this.updates.put(location, backup);
}
/**
* Add a directory
*
* @param location the directory location
*/
public void addMkdir(String location)
{
this.mkdirs.add(location);
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("InstalledFiles [warLocation=").append(this.warLocation).append(", moduleId=")
.append(this.moduleId).append(", adds=").append(this.adds).append(", updates=")
.append(this.updates).append(", mkdirs=").append(this.mkdirs).append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,220 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.module.tool;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
import org.alfresco.repo.module.ModuleDetailsImpl;
import org.alfresco.service.cmr.module.ModuleDetails;
import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileInputStream;
import de.schlichtherle.truezip.file.TFileOutputStream;
/**
* Module details helper used by the module mangement tool
*
* @author Roy Wetherall
* @author Derek Hulley
*/
public class ModuleDetailsHelper
{
/**
* Factory method to create module details from a stream of a properties file
* @param is the properties input stream, which will be closed during the call
* @return Returns the initialized module details
*/
public static ModuleDetails createModuleDetailsFromPropertiesStream(InputStream is) throws IOException
{
return createModuleDetailsFromPropertiesStream(is, null);
}
/**
* Factory method to create module details from a stream of a properties file
* @param is the properties input stream, which will be closed during the call
* @param log logger
* @return Returns the initialized module details
*/
public static ModuleDetails createModuleDetailsFromPropertiesStream(InputStream is, LogOutput log) throws IOException
{
try
{
Properties properties = new Properties();
properties.load(is);
return new ModuleDetailsImpl(properties, log);
}
finally
{
try { is.close(); } catch (Throwable e) {}
}
}
/**
* Creates a module details helper object based on a file location.
*
* @param location file location
* @return Returns the module details or null if the location points to nothing
* @throws IOException
*/
public static ModuleDetails createModuleDetailsFromPropertyLocation(String location) throws IOException
{
return createModuleDetailsFromPropertyLocation(location, null);
}
/**
* Creates a module details helper object based on a file location.
*
* @param location file location
* @param log logger
* @return Returns the module details or null if the location points to nothing
* @throws IOException
*/
public static ModuleDetails createModuleDetailsFromPropertyLocation(String location, LogOutput log) throws IOException
{
ModuleDetails result = null;
TFileInputStream is;
try
{
is = new TFileInputStream(location);
}
catch (FileNotFoundException error)
{
error.printStackTrace(System.out);
throw new ModuleManagementToolException("Unable to load module details from property file. File Not Found, " + error.getMessage(), error);
}
try
{
result = createModuleDetailsFromPropertiesStream(is, log);
}
catch (IOException exception)
{
throw new ModuleManagementToolException(
"Unable to load module details from property file." + exception.getMessage(), exception);
}
finally
{
is.close(); // ALWAYS close the stream!
}
return result;
}
/**
* Creates a module details instance based on a war location and the module id
*
* @param warLocation the war location
* @param moduleId the module id
* @return Returns the module details for the given module ID as it occurs in the WAR, or <tt>null</tt>
* if there are no module details available.
* @throws IOException
*/
public static ModuleDetails createModuleDetailsFromWarAndId(String warLocation, String moduleId) throws IOException
{
String modulePropertiesFileLocation = ModuleDetailsHelper.getModulePropertiesFileLocation(warLocation, moduleId);
return ModuleDetailsHelper.createModuleDetailsFromPropertyLocation(modulePropertiesFileLocation);
}
/**
* @param warLocation the location of the WAR file
* @param moduleId the module ID within the WAR
* @return Returns a file handle to the module properties file within the given WAR.
* The file may or may not exist.
*/
public static TFile getModuleDetailsFileFromWarAndId(String warLocation, String moduleId)
{
String location = ModuleDetailsHelper.getModulePropertiesFileLocation(warLocation, moduleId);
TFile file = new TFile(location);
return file;
}
/**
* Gets the file location
*
* @param warLocation the war location
* @param moduleId the module id
* @return the file location
*/
public static String getModulePropertiesFileLocation(String warLocation, String moduleId)
{
return warLocation + getModulePropertiesFilePathInWar(moduleId);
}
/**
* @param moduleId the module ID
* @return Returns the path of the module file within a WAR
*/
public static String getModulePropertiesFilePathInWar(String moduleId)
{
return WarHelper.MODULE_NAMESPACE_DIR + "/" + moduleId + WarHelper.MODULE_CONFIG_IN_WAR;
}
/**
* Saves the module details to the war in the correct location based on the module id
*
* @param warLocation the war location
* @param moduleDetails the module id
*/
public static void saveModuleDetails(String warLocation, ModuleDetails moduleDetails)
{
// Ensure that it is a valid set of properties
String moduleId = moduleDetails.getId();
try
{
String modulePropertiesFileLocation = getModulePropertiesFileLocation(warLocation, moduleId);
TFile file = new TFile(modulePropertiesFileLocation);
if (file.exists() == false)
{
file.createNewFile();
}
// Get all the module properties
Properties moduleProperties = moduleDetails.getProperties();
OutputStream os = new TFileOutputStream(file);
try
{
moduleProperties.store(os, null);
}
finally
{
os.close();
}
}
catch (IOException exception)
{
throw new ModuleManagementToolException(
"Unable to save module details into WAR file: \n" +
" Module: " + moduleDetails.getId() + "\n" +
" Properties: " + moduleDetails.getProperties(),
exception);
}
}
}

View File

@@ -0,0 +1,962 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.module.tool;
import de.schlichtherle.truezip.file.*;
import de.schlichtherle.truezip.fs.FsSyncException;
import de.schlichtherle.truezip.fs.archive.zip.JarDriver;
import de.schlichtherle.truezip.socket.sl.IOPoolLocator;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.module.ModuleVersionNumber;
import org.alfresco.service.cmr.module.ModuleDetails;
import org.alfresco.service.cmr.module.ModuleInstallState;
import org.safehaus.uuid.UUIDGenerator;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.Map.Entry;
/**
* Module management tool.
* <p>
* Manages the modules installed in a war file. Allows modules to be installed, updated, enabled, disabled and
* uninstalled. Information about the module installed is also available.
*
* @since 2.0
*
* @author Roy Wetherall
* @author Derek Hulley
*/
public class ModuleManagementTool implements LogOutput
{
/** Location of the default mapping properties file */
private static final String DEFAULT_FILE_MAPPING_PROPERTIES = "org/alfresco/repo/module/tool/default-file-mapping.properties";
/** Location of the AMP-specific mappings file */
private static final String FILE_MAPPING_PROPERTIES = "file-mapping.properties";
/**
* The property to add to a custom {@link #FILE_MAPPING_PROPERTIES file-mapping.properties} to inherit the default values.
* The default is <code>true</code>.
*/
private static final String PROP_INHERIT_DEFAULT = "include.default";
/** Standard directories found in the alfresco war */
public static final String BACKUP_DIR = WarHelper.MODULE_NAMESPACE_DIR+ "/backup";
/** Operations and options supperted via the command line interface to this class */
private static final String OP_INSTALL = "install";
private static final String OP_UNINSTALL = "uninstall";
private static final String OP_LIST = "list";
private static final String OPTION_VERBOSE = "-verbose";
private static final String OPTION_FORCE = "-force";
private static final String OPTION_PREVIEW = "-preview";
private static final String OPTION_NOBACKUP = "-nobackup";
private static final String OPTION_DIRECTORY = "-directory";
private static final String OPTION_PURGE = "-purge";
private static final String OPTION_HELP = "-help";
private static final int ERROR_EXIT_CODE = 1;
private static final int SUCCESS_EXIT_CODE = 0;
/** File mapping properties */
private Properties defaultFileMappingProperties;
/** Indicates the current verbose setting */
private boolean verbose = false;
WarHelper warHelper = new WarHelperImpl(this);
/**
* Constructor
*/
public ModuleManagementTool()
{
TConfig config = TConfig.get();
config.setArchiveDetector(new TArchiveDetector("war|amp", new JarDriver(IOPoolLocator.SINGLETON)));
// Load the default file mapping properties
this.defaultFileMappingProperties = new Properties();
InputStream is = this.getClass().getClassLoader().getResourceAsStream(DEFAULT_FILE_MAPPING_PROPERTIES);
try
{
this.defaultFileMappingProperties.load(is);
}
catch (IOException exception)
{
throw new ModuleManagementToolException("Unable to load default extension file mapping properties.", exception);
}
}
/**
* Indicates whether the management tool is currently in verbose reporting mode.
*
* @return true if verbose, false otherwise
*/
public boolean isVerbose()
{
return verbose;
}
/**
* Sets the verbose setting for the mangement tool
*
* @param verbose true if verbose, false otherwise
*/
public void setVerbose(boolean verbose)
{
this.verbose = verbose;
}
/**
* Installs all modules within a folder into the given WAR file.
* @throws IOException
*
* @see #installModule(String, String, boolean, boolean, boolean)
*/
public void installModules(String directory, String warFileLocation) throws IOException
{
installModules(directory, warFileLocation, false, false, true);
}
public void installModules(String directoryLocation, String warFileLocation, boolean preview, boolean forceInstall, boolean backupWAR) throws IOException
{
java.io.File dir = new java.io.File(directoryLocation);
if (dir.exists() == true)
{
if (backupWAR) {
backupWar(new TFile(warFileLocation),true);
backupWAR = false; //Set it to false so a backup doesn't occur again.
}
installModules(dir, warFileLocation, preview, forceInstall,backupWAR);
}
else
{
throw new ModuleManagementToolException("Invalid directory '" + directoryLocation + "'");
}
}
private void installModules(java.io.File dir, String warFileLocation, boolean preview, boolean forceInstall, boolean backupWAR)
{
java.io.File[] children = dir.listFiles();
if (children != null)
{
for (java.io.File child : children)
{
if (child.isFile() == true && child.getName().toLowerCase().endsWith(".amp") == true)
{
installModule(child.getPath(), warFileLocation, preview, forceInstall, backupWAR);
}
else
{
installModules(child, warFileLocation, preview, forceInstall, backupWAR);
}
}
}
}
/**
* Installs a given AMP file into a given WAR file.
*
* @see ModuleManagementTool#installModule(String, String, boolean, boolean, boolean)
*
* @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
*/
public void installModule(String ampFileLocation, String warFileLocation)
{
installModule(ampFileLocation, warFileLocation, false, false, true);
}
/**
* 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 replaced regardless 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)
{
TFile warFile = new TFile(warFileLocation);
try
{
outputVerboseMessage("Installing AMP '" + ampFileLocation + "' into WAR '" + warFileLocation + "'");
if (!warFile.exists())
{
throw new ModuleManagementToolException("The war file '" + warFile + "' does not exist.");
}
if (preview == false)
{
// Make sure the module and backup directory exisits in the WAR file
TFile moduleDir = new TFile(warFileLocation + WarHelper.MODULE_NAMESPACE_DIR);
if (moduleDir.exists() == false)
{
moduleDir.mkdir();
}
backupWar(warFile, backupWAR);
}
// Get the details of the installing module
String propertiesLocation = ampFileLocation + "/module.properties";
ModuleDetails installingModuleDetails = ModuleDetailsHelper.createModuleDetailsFromPropertyLocation(propertiesLocation, this);
if (installingModuleDetails == null)
{
throw new ModuleManagementToolException("No module.properties file has been found in the installing .amp file '" + ampFileLocation + "'");
}
String installingId = installingModuleDetails.getId();
ModuleVersionNumber installingVersion = installingModuleDetails.getModuleVersionNumber();
//A series of checks
warHelper.checkCompatibleVersion(warFile, installingModuleDetails);
warHelper.checkCompatibleEdition(warFile, installingModuleDetails);
warHelper.checkModuleDependencies(warFile, installingModuleDetails);
// Try to find an installed module by the ID
ModuleDetails installedModuleDetails = warHelper.getModuleDetailsOrAlias(warFile, installingModuleDetails);
//Check module directory exists
TFile moduleInstallDirectory = new TFile(warFileLocation + WarHelper.MODULE_NAMESPACE_DIR+ "/" + installingId);
if (preview == false && moduleInstallDirectory.exists() == false)
{
moduleInstallDirectory.mkdir();
}
uninstallIfNecessary(warFileLocation, installedModuleDetails, preview, forceInstall, installingVersion);
outputVerboseMessage("Adding files relating to version '" + installingVersion + "' of module '" + installingId + "'");
InstalledFiles installedFiles = new InstalledFiles(warFileLocation, installingId);
Properties directoryChanges = calculateChanges(ampFileLocation, warFileLocation, preview, forceInstall, installedFiles);
if (preview == false)
{
//Now actually do the changes
if (directoryChanges != null && directoryChanges.size() > 0)
{
for (Entry<Object, Object> entry : directoryChanges.entrySet())
{
TFile source = new TFile((String) entry.getKey());
TFile destination = new TFile((String) entry.getValue());
source.cp_rp(destination);
}
}
// Save the installed file list
installedFiles.save();
// Update the installed module details
installingModuleDetails.setInstallState(ModuleInstallState.INSTALLED);
installingModuleDetails.setInstallDate(new Date());
ModuleDetailsHelper.saveModuleDetails(warFileLocation, installingModuleDetails);
// Set the modified date
if (warFile.exists())
{
warFile.setLastModified(System.currentTimeMillis());
}
}
}
catch (AlfrescoRuntimeException exception)
{
throw new ModuleManagementToolException("An error was encountered during deployment of the AMP into the WAR: " + exception.getMessage(), exception);
}
catch (IOException exception)
{
throw new ModuleManagementToolException("An IO error was encountered during deployment of the AMP into the WAR", exception);
}
finally
{
try
{
TVFS.umount(warFile);
}
catch (FsSyncException e)
{
throw new ModuleManagementToolException(
"Error when attempting to unmount WAR file: "+warFile.getPath(),
e);
}
}
}
private void uninstallIfNecessary(String warFileLocation, ModuleDetails installedModuleDetails, boolean preview,
boolean forceInstall, ModuleVersionNumber installingVersion) throws IOException
{
// Now clean up the old instance
if (installedModuleDetails != null)
{
String installedId = installedModuleDetails.getId();
ModuleVersionNumber installedVersion = installedModuleDetails.getModuleVersionNumber();
int compareValue = installedVersion.compareTo(installingVersion);
if (compareValue > 0)
{
// Trying to install an earlier version of the extension
outputVerboseMessage("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
outputVerboseMessage("WARNING: The installation of this module is being forced. All files will be removed and replaced regardless of existing versions present.",false);
}
if (compareValue == 0)
{
// Trying to install the same extension version again
outputVerboseMessage("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
outputVerboseMessage("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 through 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)
TFile source = new TFile(ampFileLocation + "/" + mappingSource);
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;
}
/**
* Backsup a given WAR file.
*
* @see ModuleManagementTool#installModule(String, String, boolean, boolean, boolean)
*
* @param warFile the location of the AMP file to be installed
* @param backupWAR true if you want it to perform the backup
*/
private void backupWar(TFile warFile, boolean backupWAR) throws IOException
{
// Make a backup of the war we are going to modify
if (backupWAR == true)
{
warHelper.backup(warFile);
}
}
/**
* @return Returns the custom file mapping properties or null if they weren't overwritten
*/
private Properties getCustomFileMappings(String ampFileLocation)
{
TFile file = new TFile(ampFileLocation + "/" + FILE_MAPPING_PROPERTIES);
if (!file.exists())
{
// Nothing there
return null;
}
Properties mappingProperties = new Properties();
InputStream is = null;
try
{
is = new BufferedInputStream(new TFileInputStream(file));
mappingProperties.load(is);
}
catch (IOException exception)
{
throw new ModuleManagementToolException("Unable to load default extension file mapping properties.", exception);
}
finally
{
if (is != null)
{
try { is.close(); } catch (Throwable e ) {}
}
}
return mappingProperties;
}
/**
* Cleans the WAR file of all files relating to the currently installed version of the the Module.
*
* @param warFileLocation the war file location
* @param moduleId the module id
* @param preview indicates whether this is a preview installation
* @param purge Fully delete all files (including those marked "PRESERVED")
* @throws IOException
*/
public void uninstallModule(String moduleId,String warFileLocation, boolean preview, boolean purge) throws IOException
{
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
TFile modified = new TFile(warFileLocation + update.getKey());
TFile backup = new TFile(warFileLocation + update.getValue());
backup.cp_rp(modified);
backup.rm();
}
outputVerboseMessage("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);
}
/**
* Removes a file from the given location in the war file.
*
* @param warLocation the war file location
* @param filePath the path to the file that is to be deleted
* @param preview indicates whether this is a preview install
*/
private void removeFile(String warLocation, String filePath, boolean preview)
{
TFile removeFile = new TFile(warLocation + filePath);
if (removeFile.exists() == true)
{
outputVerboseMessage("Removing file '" + filePath + "' from war", true);
if (preview == false)
{
removeFile.delete();
}
}
else
{
outputVerboseMessage("The file '" + filePath + "' was expected for removal but was not present in the war", true);
}
}
/**
* Copies a file from the AMP location to the correct location in the WAR, interating on directories where appropraite.
*
* @param ampFileLocation the AMP file location
* @param warFileLocation the WAR file location
* @param sourceDir the directory in the AMP to copy from. 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 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
*/
private void calculateCopyToWar(String ampFileLocation, String warFileLocation, String sourceDir, String destinationDir, InstalledFiles installedFiles, boolean preview, boolean forceInstall)
throws IOException
{
if (sourceDir.length() == 0 || !sourceDir.startsWith("/"))
{
throw new IllegalArgumentException("sourceDir must start with '/'");
}
if (destinationDir.length() == 0 || !destinationDir.startsWith("/"))
{
throw new IllegalArgumentException("destinationDir must start with '/'");
}
// Handle source and destination if they are just the root '/'
if (sourceDir.equals("/"))
{
sourceDir = "";
}
if (destinationDir.equals("/"))
{
destinationDir = "";
}
String sourceLocation = ampFileLocation + sourceDir;
TFile ampConfig = new TFile(sourceLocation);
java.io.File[] files = ampConfig.listFiles();
if (files != null)
{
for (java.io.File sourceChild : files)
{
String destinationFileLocation = warFileLocation + destinationDir + "/" + sourceChild.getName();
TFile destinationChild = new TFile(destinationFileLocation);
if (sourceChild.isFile() == true)
{
String backupLocation = null;
boolean createFile = false;
if (destinationChild.exists() == false)
{
createFile = true;
}
else
{
if (forceInstall)
{
// Backup file about to be updated
backupLocation = BACKUP_DIR + "/" + generateGuid() + ".bin";
if (preview == false)
{
//Create the directory if it doesn't exist.
TFile backupLocationDirectory = new TFile(warFileLocation+ BACKUP_DIR);
if (!backupLocationDirectory.exists())
{
backupLocationDirectory.mkdir();
}
//Backup the file
TFile backupFile = new TFile(warFileLocation + backupLocation);
destinationChild.cp_rp(backupFile);
}
} 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.");
}
}
if (createFile == true)
{
installedFiles.addAdd(destinationDir + "/" + sourceChild.getName());
this.outputVerboseMessage("File '" + destinationDir + "/" + sourceChild.getName() + "' added to war from amp", true);
}
else
{
installedFiles.addUpdate(destinationDir + "/" + sourceChild.getName(), backupLocation);
this.outputMessage("WARNING: The file '" + destinationDir + "/" + sourceChild.getName() + "' is being overwritten by this module. The original has been backed-up to '" + backupLocation + "'", true);
}
}
else
{
boolean mkdir = false;
if (destinationChild.exists() == false)
{
mkdir = true;
}
calculateCopyToWar(ampFileLocation, warFileLocation, sourceDir + "/" + sourceChild.getName(),
destinationDir + "/" + sourceChild.getName(), installedFiles, preview, forceInstall);
if (mkdir == true)
{
installedFiles.addMkdir(destinationDir + "/" + sourceChild.getName());
this.outputVerboseMessage("Directory '" + destinationDir + "/" + sourceChild.getName() + "' added to war", true);
}
}
}
}
}
/**
* @throws UnsupportedOperationException
*/
public void disableModule(String moduleId, String warLocation)
{
throw new UnsupportedOperationException("Disable module is not currently supported");
}
/**
* @throws UnsupportedOperationException
*/
public void enableModule(String moduleId, String warLocation)
{
throw new UnsupportedOperationException("Enable module is not currently supported");
}
/**
* Lists all the currently installed modules in the WAR
*
* @param warLocation the war location
* @throws ModuleManagementToolException
*/
public void listModules(String warLocation)
{
boolean previous = this.verbose;
this.verbose = true;
try
{
List<ModuleDetails> modulesFound = warHelper.listModules(new TFile(warLocation));
if (modulesFound.size() < 1)
{
outputVerboseMessage("No modules are installed in this WAR file");
}
for (Iterator<ModuleDetails> iterator = modulesFound.iterator(); iterator.hasNext(); ) {
ModuleDetails moduleDetails = iterator.next();
outputVerboseMessage("Module '" + moduleDetails.getId() + "' installed in '" + warLocation + "'");
outputVerboseMessage(" Title: " + moduleDetails.getTitle(), true);
outputVerboseMessage(" Version: " + moduleDetails.getModuleVersionNumber(), true);
outputVerboseMessage(" Install Date: " + moduleDetails.getInstallDate(), true);
outputVerboseMessage(" Description: " + moduleDetails.getDescription(), true);
}
}
finally
{
this.verbose = previous;
}
}
/**
* Outputs a message the console (in verbose mode).
*
* @param message the message to output
*/
private void outputVerboseMessage(String message)
{
outputMessage(message, false, false, false);
}
/**
* Outputs a message the console (in verbose mode).
*
* @param message the message to output
*/
private void outputErrorMessage(String message)
{
outputMessage(message, false, true, false);
}
/**
* Outputs a message the console (in verbose mode).
*
* @param message the message to output
* @param indent indicates that the message should be formated with an indent
*/
private void outputVerboseMessage(String message, boolean indent)
{
outputMessage(message, indent, false, false);
}
/**
* Outputs a message to the console regardless of the verbose setting.
*
* @param message the message to output
* @param indent indicates that the message should be formated with an indent
*/
private void outputMessage(String message, boolean indent)
{
outputMessage(message, indent, false, true);
}
/**
* Outputs a message the console. Errors are always output, but others are only output in verbose mode.
*
* @param message the message to output
* @param indent indicates that the message should be formated with an indent
* @param error indicates that the message is an error.
* @param stdout indicates that the message should output to the console regardless of verbose setting
*/
private void outputMessage(String message, boolean indent, boolean error, boolean stdout)
{
if (indent == true)
{
message = " - " + message;
}
if (error)
{
System.err.println(message);
}
else if (this.verbose == true || stdout == true)
{
System.out.println(message);
}
}
/**
* Main
*
* @param args command line interface arguments
*/
public static void main(String[] args)
{
if (args.length <= 1)
{
outputUsage();
System.exit(ERROR_EXIT_CODE);
}
ModuleManagementTool manager = new ModuleManagementTool();
String operation = args[0];
try
{
if (operation.equals(OPTION_HELP) == true)
{
outputUsage();
System.exit(SUCCESS_EXIT_CODE);
}
else if (operation.equals(OP_INSTALL) == true)
{
if (args.length < 3)
{
throw new UsageException(OP_INSTALL + " requires at least 3 arguments.");
}
String aepFileLocation = args[1];
String warFileLocation = args[2];
boolean forceInstall = false;
boolean previewInstall = false;
boolean backup = true;
boolean directory = false;
if (args.length > 3)
{
for (int i = 3; i < args.length; i++)
{
String option = args[i];
if (OPTION_VERBOSE.equals(option) == true)
{
manager.setVerbose(true);
}
else if (OPTION_FORCE.equals(option) == true)
{
forceInstall = true;
}
else if (OPTION_PREVIEW.equals(option) == true)
{
previewInstall = true;
manager.setVerbose(true);
}
else if (OPTION_NOBACKUP.equals(option) == true)
{
backup = false;
}
else if (OPTION_DIRECTORY.equals(option) == true)
{
directory = true;
}
}
}
try
{
if (directory == false)
{
// Install the module
manager.installModule(aepFileLocation, warFileLocation, previewInstall, forceInstall, backup);
}
else
{
// Install the modules from the directory
manager.installModules(aepFileLocation, warFileLocation, previewInstall, forceInstall, backup);
}
}
catch (IOException error)
{
throw new ModuleManagementToolException(error.getMessage());
}
System.exit(SUCCESS_EXIT_CODE);
}
else if (OP_LIST.equals(operation) == true)
{
if (args.length != 2)
{
throw new UsageException(OP_LIST + " requires 2 arguments.");
}
// List the installed modules
String warFileLocation = args[1];
manager.listModules(warFileLocation);
System.exit(SUCCESS_EXIT_CODE);
}
else if (OP_UNINSTALL.equals(operation) == true)
{
if (args.length < 3)
{
throw new UsageException(OP_UNINSTALL + " requires at least 3 arguments.");
}
String moduleId = args[1];
String warLocation = args[2];
boolean purge = false;
boolean preview = false;
if (args.length >= 4)
{
for (int i = 3; i < args.length; i++)
{
String option = args[i];
if (OPTION_PURGE.equals(option) == true)
{
purge = true;
}
if (OPTION_PREVIEW.equals(option) == true)
{
preview = true;
manager.setVerbose(true);
}
}
}
manager.setVerbose(true);
manager.uninstallModule(moduleId, warLocation,preview, purge);
System.exit(SUCCESS_EXIT_CODE);
}
else
{
throw new UsageException("Unknown operation " + operation + ".");
}
}
catch (UsageException e)
{
manager.outputErrorMessage("Usage error: " + e.getMessage());
outputUsage();
System.exit(ERROR_EXIT_CODE);
}
catch (ModuleManagementToolException e)
{
// These are user-friendly
manager.outputErrorMessage(e.getMessage());
System.exit(ERROR_EXIT_CODE);
}
catch (IOException error)
{
manager.outputErrorMessage(error.getMessage());
System.exit(ERROR_EXIT_CODE);
}
}
/**
* Generates a GUID, avoiding undesired imports.
*/
private static String generateGuid()
{
return UUIDGenerator.getInstance().generateTimeBasedUUID().toString();
}
/**
* Outputs the module management tool usage
*/
private static void outputUsage()
{
System.out.println("Module managment tool available commands:");
System.out.println("-----------------------------------------------------------\n");
System.out.println("install: Installs a AMP file(s) into an Alfresco WAR file, updates if an older version is already installed.");
System.out.println("usage: install <AMPFileLocation> <WARFileLocation> [options]");
System.out.println("valid options: ");
System.out.println(" -verbose : enable verbose output");
System.out.println(" -directory : indicates that the amp file location specified is a directory.");
System.out.println(" All amp files found in the directory and its sub directories are installed.");
System.out.println(" -force : forces installation of AMP regardless of currently installed module version");
System.out.println(" -preview : previews installation of AMP without modifying WAR file");
System.out.println(" -nobackup : indicates that no backup should be made of the WAR\n");
System.out.println("-----------------------------------------------------------\n");
System.out.println("list: Lists all the modules currently installed in an Alfresco WAR file.");
System.out.println("usage: list <WARFileLocation>\n");
System.out.println("-----------------------------------------------------------\n");
System.out.println("uninstall: Uninstalls a module from the Alfresco WAR file.");
System.out.println("usage: uninstall <ModuleId> <WARFileLocation>\n");
System.out.println("-----------------------------------------------------------\n");
}
@Override
public void info(Object message)
{
outputVerboseMessage(String.valueOf(message));
}
private static class UsageException extends Exception
{
private static final long serialVersionUID = 1L;
public UsageException(String message) {
super(message);
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.module.tool;
import org.alfresco.service.cmr.module.ModuleDetails;
import de.schlichtherle.truezip.file.TFile;
import java.io.IOException;
import java.util.List;
import java.util.jar.Manifest;
/**
* Performs various actions on a war file or exploded war directory
*
* @author Gethin James
*/
public interface WarHelper
{
public static final String MODULE_NAMESPACE_DIR = "/WEB-INF/classes/alfresco/module";
public static final String MODULE_CONFIG_IN_WAR = "/module.properties";
/**
* Gets the module details or an available alias
* @param war a valid war file or exploded directory from a war
* @param installingModuleDetails ModuleDetails
* @return ModuleDetails
*/
public ModuleDetails getModuleDetailsOrAlias(TFile war, ModuleDetails installingModuleDetails);
/**
* Checks the dependencies of this module
* @param war TFile
* @param installingModuleDetails ModuleDetails
*/
public void checkModuleDependencies(TFile war, ModuleDetails installingModuleDetails);
/**
* Checks to see if the module is compatible with the version of Alfresco.
*
* @param war a valid war file or exploded directory from a war
*/
public void checkCompatibleVersion(TFile war, ModuleDetails installingModuleDetails);
/**
* This checks to see if the module that is being installed is compatible with the war.
* If not module edition is specfied then it will just return. However, if an edition is specified and it doesn't match
* then an error is thrown.
* @param war a valid war file or exploded directory from a war
* @param installingModuleDetails ModuleDetails
*/
public void checkCompatibleEdition(TFile war, ModuleDetails installingModuleDetails);
/**
* Indicates if the war file specified is a "Share" war. The default is FALSE
* Returns true if the Share war manifest states its a share war.
* @since 3.4.11,4.1.1,Community 4.2
*
* @param war TFile
* @return boolean - true if it is a share war
*/
public boolean isShareWar(TFile war);
/**
* Lists all the currently installed modules in the WAR
* @since 5.1
* @param war the war
* @return an unordered list of module details.
* @throws ModuleManagementToolException
*/
List<ModuleDetails> listModules(TFile war);
/**
* Backs up a given file or directory.
*
* @since 5.1
* @param file the file to backup
* @return the absolute path to the backup file.
*/
public String backup(TFile file) throws IOException;
/**
* Finds a war manifest file.
* @since 5.1
* @param war the war
* @return Manifest
* @throws ModuleManagementToolException
*/
public Manifest findManifest(TFile war) throws ModuleManagementToolException;
}

View File

@@ -0,0 +1,462 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.module.tool;
import de.schlichtherle.truezip.file.TArchiveDetector;
import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileInputStream;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.module.ModuleDetailsImpl;
import org.alfresco.service.cmr.module.ModuleDependency;
import org.alfresco.service.cmr.module.ModuleDetails;
import org.alfresco.util.VersionNumber;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
/**
* Performs logic for the Module Management Tool.
*
* @author Gethin James
*/
public class WarHelperImpl implements WarHelper
{
public static final String VERSION_PROPERTIES = "/WEB-INF/classes/alfresco/version.properties";
public static final String MANIFEST_FILE = "/META-INF/MANIFEST.MF";
//see http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Main%20Attributes
public static final String MANIFEST_SPECIFICATION_TITLE = "Specification-Title";
public static final String MANIFEST_SPECIFICATION_VERSION = "Specification-Version";
public static final String MANIFEST_IMPLEMENTATION_TITLE = "Implementation-Title";
public static final String MANIFEST_SHARE = "Alfresco Share";
public static final String MANIFEST_COMMUNITY = "Community";
protected static final String REGEX_NUMBER_OR_DOT = "[0-9\\.]*";
private LogOutput log = null;
public WarHelperImpl(LogOutput log)
{
super();
this.log = log;
}
@Override
public void checkCompatibleVersion(TFile war, ModuleDetails installingModuleDetails)
{
//Version check
TFile propsFile = new TFile(war+VERSION_PROPERTIES);
if (propsFile != null && propsFile.exists())
{
log.info("INFO: Checking the war version using "+VERSION_PROPERTIES);
Properties warVers = loadProperties(propsFile);
VersionNumber warVersion = new VersionNumber(warVers.getProperty("version.major")+"."+warVers.getProperty("version.minor")+"."+warVers.getProperty("version.revision"));
checkVersions(warVersion, installingModuleDetails);
}
else
{
log.info("INFO: Checking the war version using the manifest.");
checkCompatibleVersionUsingManifest(war,installingModuleDetails);
}
}
/**
* Checks if the module is compatible using the entry in the manifest. This is more accurate and works for both alfresco.war and share.war, however
* valid manifest entries weren't added until 3.4.11, 4.1.1 and Community 4.2
* @param war TFile
* @param installingModuleDetails ModuleDetails
*/
protected void checkCompatibleVersionUsingManifest(TFile war, ModuleDetails installingModuleDetails)
{
String version = findManifestArtibute(war, MANIFEST_SPECIFICATION_VERSION);
if (version != null && version.length() > 0)
{
if (version.matches(REGEX_NUMBER_OR_DOT)) {
VersionNumber warVersion = new VersionNumber(version);
checkVersions(warVersion, installingModuleDetails);
}
else
{
//A non-numeric version number. Currently our VersionNumber class doesn't support Strings in the version
String edition = findManifestArtibute(war, MANIFEST_IMPLEMENTATION_TITLE);
if (edition.endsWith(MANIFEST_COMMUNITY))
{
//If it's a community version, so don't worry about it
log.info("WARNING: Community edition war detected, the version number is non-numeric so we will not validate it.");
}
else
{
throw new ModuleManagementToolException("Invalid version number specified: "+ version);
}
}
}
else
{
log.info("WARNING: No version information detected in war, therefore version validation is disabled, continuing anyway. Is this war prior to 3.4.11, 4.1.1 and Community 4.2 ?");
}
}
/**
* Finds a single attribute from a war manifest file.
* @param war the war
* @param attributeName key name of attribute
* @return attribute value
* @throws ModuleManagementToolException
*/
protected String findManifestArtibute(TFile war, String attributeName) throws ModuleManagementToolException {
Manifest manifest = findManifest(war);
Attributes attribs = manifest.getMainAttributes();
return attribs.getValue(attributeName);
}
/**
* Finds a single attribute from a war manifest file.
* @param war the war
* @return Manifest
* @throws ModuleManagementToolException
*/
@Override
public Manifest findManifest(TFile war) throws ModuleManagementToolException {
InputStream is = null;
try
{
is = new TFileInputStream(war+MANIFEST_FILE);
Manifest manifest = new Manifest(is);
return manifest;
}
catch (IOException e)
{
throw new ModuleManagementToolException("Unabled to read a manifest for the war file: "+ war);
}
finally
{
if (is != null)
{
try { is.close(); } catch (Throwable e ) {}
}
}
}
/**
* Compares the version information with the module details to see if their valid. If they are invalid then it throws an exception.
* @param warVersion VersionNumber
* @param installingModuleDetails ModuleDetails
* @throws ModuleManagementToolException
*/
private void checkVersions(VersionNumber warVersion, ModuleDetails installingModuleDetails) throws ModuleManagementToolException
{
if(warVersion.compareTo(installingModuleDetails.getRepoVersionMin())==-1) {
throw new ModuleManagementToolException("The module ("+installingModuleDetails.getTitle()+") must be installed on a war version equal to or greater than "
+installingModuleDetails.getRepoVersionMin()+". This war is version: "+warVersion+".");
}
if(warVersion.compareTo(installingModuleDetails.getRepoVersionMax())==1) {
throw new ModuleManagementToolException("The module ("+installingModuleDetails.getTitle()+") cannot be installed on a war version greater than "
+installingModuleDetails.getRepoVersionMax()+". This war is version: "+warVersion+".");
}
}
@Override
public void checkCompatibleEdition(TFile war, ModuleDetails installingModuleDetails)
{
List<String> installableEditions = installingModuleDetails.getEditions();
if (installableEditions != null && installableEditions.size() > 0) {
TFile propsFile = new TFile(war+VERSION_PROPERTIES);
if (propsFile != null && propsFile.exists())
{
Properties warVers = loadProperties(propsFile);
String warEdition = warVers.getProperty("version.edition");
for (String edition : installableEditions)
{
if (warEdition.equalsIgnoreCase(edition))
{
return; //successful match.
}
}
throw new ModuleManagementToolException("The module ("+installingModuleDetails.getTitle()
+") can only be installed in one of the following editions"+installableEditions);
} else {
checkCompatibleEditionUsingManifest(war,installingModuleDetails);
}
}
}
/**
* Checks to see if the module that is being installed is compatible with the war, (using the entry in the manifest).
* This is more accurate and works for both alfresco.war and share.war, however
* valid manifest entries weren't added until 3.4.11, 4.1.1 and Community 4.2
* @param war TFile
* @param installingModuleDetails ModuleDetails
*/
public void checkCompatibleEditionUsingManifest(TFile war, ModuleDetails installingModuleDetails)
{
List<String> installableEditions = installingModuleDetails.getEditions();
if (installableEditions != null && installableEditions.size() > 0) {
String warEdition = findManifestArtibute(war, MANIFEST_IMPLEMENTATION_TITLE);
if (warEdition != null && warEdition.length() > 0)
{
warEdition = warEdition.toLowerCase();
for (String edition : installableEditions)
{
if (warEdition.endsWith(edition.toLowerCase()))
{
return; //successful match.
}
}
throw new ModuleManagementToolException("The module ("+installingModuleDetails.getTitle()
+") can only be installed in one of the following editions"+installableEditions);
} else {
log.info("WARNING: No edition information detected in war, edition validation is disabled, continuing anyway. Is this war prior to 3.4.11, 4.1.1 and Community 4.2 ?");
}
}
}
@Override
public void checkModuleDependencies(TFile war, ModuleDetails installingModuleDetails)
{
// 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 = getModuleDetails(war, 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);
}
}
@Override
public ModuleDetails getModuleDetailsOrAlias(TFile war, ModuleDetails installingModuleDetails)
{
ModuleDetails installedModuleDetails = getModuleDetails(war, installingModuleDetails.getId());
if (installedModuleDetails == null)
{
// It might be there as one of the aliases
List<String> installingAliases = installingModuleDetails.getAliases();
for (String installingAlias : installingAliases)
{
ModuleDetails installedAliasModuleDetails = getModuleDetails(war, 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 '" + installingModuleDetails + "'", false);
break;
}
}
return installedModuleDetails;
}
@Override
public boolean isShareWar(TFile warFile)
{
if (!warFile.exists())
{
throw new ModuleManagementToolException("The war file '" + warFile + "' does not exist.");
}
String title = findManifestArtibute(warFile, MANIFEST_SPECIFICATION_TITLE);
if (MANIFEST_SHARE.equals(title)) return true; //It is share
return false; //default
}
/**
* Lists all the currently installed modules in the WAR
*
* @param war the war
* @throws ModuleManagementToolException
*/
@Override
public List<ModuleDetails> listModules(TFile war)
{
List<ModuleDetails> moduleDetails = new ArrayList<>();
boolean moduleFound = false;
TFile moduleDir = new TFile(war, WarHelper.MODULE_NAMESPACE_DIR);
if (moduleDir.exists() == false)
{
return moduleDetails; //empty
}
java.io.File[] dirs = moduleDir.listFiles();
if (dirs != null && dirs.length != 0)
{
for (java.io.File dir : dirs)
{
if (dir.isDirectory() == true)
{
TFile moduleProperties = new TFile(dir.getPath() + WarHelper.MODULE_CONFIG_IN_WAR);
if (moduleProperties.exists() == true)
{
InputStream is = null;
try
{
moduleFound = true;
is = new TFileInputStream(moduleProperties);
moduleDetails.add(ModuleDetailsHelper.createModuleDetailsFromPropertiesStream(is));
}
catch (AlfrescoRuntimeException exception)
{
throw new ModuleManagementToolException("Unable to open module properties file '" + moduleProperties.getPath() + "' " + exception.getMessage(), exception);
}
catch (IOException exception)
{
throw new ModuleManagementToolException("Unable to open module properties file '" + moduleProperties.getPath() + "'", exception);
}
finally
{
if (is != null)
{
try { is.close(); } catch (Throwable e ) {}
}
}
}
}
}
}
return moduleDetails;
}
/**
* Backs up a given file or directory.
*
* @param file the file to backup
* @return the absolute path to the backup file.
*/
@Override
public String backup(TFile file) throws IOException
{
String backupLocation = file.getAbsolutePath()+"-" + System.currentTimeMillis() + ".bak";
if (file.isArchive())
{
log.info("Backing up file...");
TFile source = new TFile(file.getAbsolutePath(), TArchiveDetector.NULL);
TFile backup = new TFile(backupLocation, TArchiveDetector.NULL);
source.cp_rp(backup); //Just copy the file
}
else
{
log.info("Backing up DIRECTORY...");
TFile backup = new TFile(backupLocation);
file.cp_rp(backup); //Copy the directory
}
log.info("The back up is at '" + backupLocation + "'");
return backupLocation;
}
/**
* Gets the module details for the specified module from the war.
* @param war a valid war file or exploded directory from a war
* @param moduleId String
* @return ModuleDetails
*/
protected ModuleDetails getModuleDetails(TFile war, String moduleId)
{
ModuleDetails moduleDets = null;
TFile theFile = getModuleDetailsFile(war, moduleId);
if (theFile != null && theFile.exists())
{
moduleDets = new ModuleDetailsImpl(loadProperties(theFile));
}
return moduleDets;
}
/**
* Reads a .properites file from the war and returns it as a Properties object
* @param propertiesFile Path to the properties file (including .properties)
* @return Properties object or null
*/
private Properties loadProperties(TFile propertiesFile)
{
Properties result = null;
InputStream is = null;
try
{
if (propertiesFile.exists())
{
is = new TFileInputStream(propertiesFile);
result = new Properties();
result.load(is);
}
}
catch (IOException exception)
{
throw new ModuleManagementToolException("Unable to load properties from the war file; "+propertiesFile.getPath(), exception);
}
finally
{
if (is != null)
{
try { is.close(); } catch (Throwable e ) {}
}
}
return result;
}
private TFile getModuleDetailsFile(TFile war, String moduleId)
{
return new TFile(war.getAbsolutePath()+MODULE_NAMESPACE_DIR+ "/" + moduleId+MODULE_CONFIG_IN_WAR);
}
}

View File

@@ -0,0 +1,9 @@
# The default AMP => WAR file mappings
/config=/WEB-INF/classes
/lib=/WEB-INF/lib
/licenses=/WEB-INF/licenses
/web/jsp=/jsp
/web/css=/css
/web/images=/images
/web/scripts=/scripts
/web/php=/php

View File

@@ -0,0 +1,43 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco;
import org.junit.experimental.categories.Categories;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/**
* All MMT Tool project UNIT test classes (no application context) should be added to this test suite.
*/
@RunWith(Categories.class)
@Suite.SuiteClasses({
org.alfresco.repo.module.tool.ModuleManagementToolTest.class,
org.alfresco.repo.module.tool.WarHelperImplTest.class,
org.alfresco.repo.module.tool.ModuleServiceImplTest.class
})
public class AllMmtUnitTestSuite
{
}

View File

@@ -0,0 +1,507 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.module.tool;
import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileInputStream;
import de.schlichtherle.truezip.file.TVFS;
import junit.framework.TestCase;
import org.alfresco.service.cmr.module.ModuleDetails;
import org.alfresco.util.TempFileProvider;
import org.springframework.util.FileCopyUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* @see org.alfresco.repo.module.tool.ModuleManagementTool
*
* @author Roy Wetherall
* @author Derek Hulley
*/
public class ModuleManagementToolTest extends TestCase
{
private ModuleManagementTool manager = new ModuleManagementTool();
static final int BUFFER = 2048;
public void testBasicInstall()
throws Exception
{
manager.setVerbose(true);
String warLocation = getFileLocation(".war", "module/test.war");
String ampLocation = getFileLocation(".amp", "module/test_v1.amp");
String ampV2Location = getFileLocation(".amp", "module/test_v2.amp");
installerSharedTests(warLocation, ampLocation, ampV2Location);
}
private void installerSharedTests(String warLocation, String ampLocation, String ampV2Location)
{
// Initial install of module
this.manager.installModule(ampLocation, warLocation);
// Check that the war has been modified correctly
List<String> files = new ArrayList<String>(10);
files.add("/WEB-INF/classes/alfresco/module/test/module.properties");
files.add("/WEB-INF/classes/alfresco/module/test/modifications.install");
files.add("/WEB-INF/lib/test.jar");
files.add("/WEB-INF/classes/alfresco/module/test/module-context.xml");
files.add("/WEB-INF/classes/alfresco/module/test");
files.add("/WEB-INF/licenses/license.txt");
files.add("/scripts/test.js");
files.add("/images/test.jpg");
files.add("/jsp/test.jsp");
files.add("/css/test.css");
files.add("/extra.txt");
checkForFileExistance(warLocation, files);
// Check the intstalled files
InstalledFiles installed0 = new InstalledFiles(warLocation, "test");
installed0.load();
assertNotNull(installed0);
assertEquals(9, installed0.getAdds().size());
//assertEquals(1, installed0.getMkdirs().size());
// Try and install same version
try
{
this.manager.installModule(ampLocation, warLocation);
// Already installed is now a Warning rather than an Error and is now non fatal
// fail("The module is already installed so an exception should have been raised since we are not forcing an overwite");
}
catch(ModuleManagementToolException exception)
{
// Pass
}
// Install a later version
this.manager.installModule(ampV2Location, warLocation);
// Check that the war has been modified correctly
List<String> files2 = new ArrayList<String>(12);
files.add("/WEB-INF/classes/alfresco/module/test/module.properties");
files.add("/WEB-INF/classes/alfresco/module/test/modifications.install");
files2.add("/WEB-INF/lib/test.jar");
files2.add("/WEB-INF/classes/alfresco/module/test/module-context.xml");
files2.add("/WEB-INF/classes/alfresco/module/test");
files2.add("/WEB-INF/licenses/license.txt");
files2.add("/scripts/test2.js");
files2.add("/scripts/test3.js");
files2.add("/images/test.jpg");
files2.add("/css/test.css");
files2.add("/WEB-INF/classes/alfresco/module/test/version2");
files2.add("/WEB-INF/classes/alfresco/module/test/version2/version2-context.xml");
checkForFileExistance(warLocation, files2);
List<String> files3 = new ArrayList<String>(2);
files3.add("/scripts/test.js");
files3.add("/jsp/test.jsp");
files3.add("/extra.txt");
checkForFileNonExistance(warLocation, files3);
// Check the intstalled files
InstalledFiles installed1 = new InstalledFiles(warLocation, "test");
installed1.load();
assertNotNull(installed1);
assertEquals(8, installed1.getAdds().size());
assertEquals(1, installed1.getMkdirs().size());
assertEquals(0, installed1.getUpdates().size());
/**
* Try and install an earlier version over a later version
*/
try
{
this.manager.installModule(ampLocation, warLocation);
//fail("A later version of this module is already installed so an exception should have been raised since we are not forcing an overwite");
//this is now a warning rather than an error
// Check that the war has not been modified
checkForFileExistance(warLocation, files2);
checkForFileNonExistance(warLocation, files3);
}
catch(ModuleManagementToolException exception)
{
// Pass
}
}
public void testBasicFolderInstall() throws Exception
{
manager.setVerbose(true);
String warDirectory = extractToDir(".war", "module/test.war");
String ampDirectory = extractToDir(".amp", "module/test_v1.amp");
String ampV2Directory = getFileLocation(".amp", "module/test_v2.amp");
assertNotNull(warDirectory);
assertNotNull(ampDirectory);
assertNotNull(ampV2Directory);
installerSharedTests(warDirectory, ampDirectory, ampV2Directory);
//Now try it on share
warDirectory = extractToDir(".war", "module/share-3.4.11.war");
assertNotNull(warDirectory);
assertNotNull(ampDirectory);
this.manager.installModule(ampDirectory, warDirectory);
warDirectory = extractToDir(".war", "module/share-4.2.a.war");
assertNotNull(warDirectory);
String ampV2Location = getFileLocation(".amp", "module/test_v6.amp");
this.manager.installModule(ampV2Location, warDirectory);
}
public void testDependencySuccess() throws Exception
{
manager.setVerbose(true);
String warLocation = getFileLocation(".war", "module/test.war");
String testAmpV1Location = getFileLocation(".amp", "module/test_v1.amp");
String testAmpV2Location = getFileLocation(".amp", "module/test_v2.amp");
String testAmpDepV1Location = getFileLocation(".amp", "module/dependent_on_test_v1.amp");
String testAmpDepV2Location = getFileLocation(".amp", "module/dependent_on_test_v2.amp");
// Install V1
this.manager.installModule(testAmpV1Location, warLocation, false, false, false);
// Install the module dependent on test_v1
this.manager.installModule(testAmpDepV1Location, warLocation, false, false, false);
try
{
// Attempt to upgrade the dependent module
this.manager.installModule(testAmpDepV2Location, warLocation, false, false, false);
fail("Failed to detect inadequate dependency on the test amp");
}
catch (ModuleManagementToolException e)
{
System.out.println("Expected: " + e.getMessage());
}
// Install the test_v2
this.manager.installModule(testAmpV2Location, warLocation, false, false, false);
// The dependent module should now go in
this.manager.installModule(testAmpDepV2Location, warLocation, false, false, false);
}
public void testPreviewInstall()
throws Exception
{
manager.setVerbose(true);
String warLocation = getFileLocation(".war", "module/test.war");
String ampLocation = getFileLocation(".amp", "module/test_v1.amp");
// Initial install of module
this.manager.installModule(ampLocation, warLocation, true, false, true);
// TODO need to prove that the war file has not been updated in any way
}
public void testUninstall()
throws Exception
{
manager.setVerbose(true);
String warLocation = getFileLocation(".war", "module/test.war");
String ampLocation = getFileLocation(".amp", "module/test_v1.amp");
// Initial install of module
this.manager.installModule(ampLocation, warLocation, false, false, false);
this.manager.listModules(warLocation);
this.manager.uninstallModule("test", warLocation, false, false);
List<String> files = new ArrayList<String>(10);
files.add("/WEB-INF/classes/alfresco/module/test/module.properties");
files.add("/WEB-INF/classes/alfresco/module/test/modifications.install");
files.add("/WEB-INF/lib/test.jar");
files.add("/WEB-INF/classes/alfresco/module/test/module-context.xml");
checkForFileNonExistance(warLocation, files);
}
public void testForcedInstall()
throws Exception
{
manager.setVerbose(true);
String warLocation = getFileLocation(".war", "module/alfresco-4.2.c.war");
String ampLocation = getFileLocation(".amp", "module/test_v4.amp");
try
{
// Initial install of module
this.manager.installModule(ampLocation, warLocation, false, false, false); //not forced
fail("Failed to detect existing files in the amp");
}
catch (ModuleManagementToolException e)
{
assertTrue(e.getMessage().contains("The amp will overwrite an existing file"));
}
String ampv2Location = getFileLocation(".amp", "module/test_v2.amp");
warLocation = getFileLocation(".war", "module/alfresco-4.2.c.war"); //Get a new war file
this.manager.installModule(ampLocation, warLocation, false, true, false); //install v1
this.manager.installModule(ampv2Location, warLocation, false, true, false); //install v2
//install another amp that replaces the same files
ampLocation = getFileLocation(".amp", "module/test_v4.amp");
warLocation = getFileLocation(".war", "module/alfresco-4.2.c.war"); //Get a new war file
String amp5Location = getFileLocation(".amp", "module/test_v7.amp"); //new amp that overides existing files
this.manager.installModule(ampLocation, warLocation, false, true, false);
this.manager.installModule(amp5Location, warLocation, false, true, false);
}
public void testInstallFromDir()
throws Exception
{
manager.setVerbose(true);
String warLocation = getFileLocation(".war", "module/test.war");
String ampLocation = getFileLocation(".amp", "module/test_v1.amp");
String ampV2Location = getFileLocation(".amp", "module/test_v2.amp");
int index = ampV2Location.lastIndexOf(File.separator);
System.out.println(index);
String directoryLocation = ampV2Location.substring(0, index);
try
{
this.manager.installModules(directoryLocation, warLocation);
}
catch (ModuleManagementToolException exception)
{
exception.printStackTrace();
System.out.println("Expected failure: " + exception.getMessage());
}
}
public void testExistingFilesInWar() throws Exception
{
manager.setVerbose(true);
String warLocation = getFileLocation(".war", "module/test.war"); //Version 4.0.1
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;}");
this.manager.installModule(ampLocation, warLocation, false, true, false); //install it again
}
public void testWhiteSpaceInCustomMapping()
throws Exception
{
manager.setVerbose(true);
String warLocation = getFileLocation(".war", "module/test.war");
String ampLocation = getFileLocation(".amp", "module/test_v3.amp");
// Initial install of module
this.manager.installModule(ampLocation, warLocation, false, false, true);
List<String> files = new ArrayList<String>(10);
files.add("/WEB-INF/classes/alfresco/module/test/module.properties");
files.add("/WEB-INF/classes/alfresco/module/test/modifications.install");
files.add("/WEB-INF/lib/test.jar");
files.add("/WEB-INF/classes/alfresco/module/test/module-context.xml");
files.add("/images/test.jpg");
files.add("/css/test.css");
files.add("/extra.txt");
checkForFileExistance(warLocation, files);
}
public void testList() throws Exception
{
String warLocation = getFileLocation(".war", "module/test.war");
this.manager.listModules(warLocation);
}
public void testListAndInstall() throws Exception {
String warLocation = getFileLocation(".war", "module/test.war");
String ampLocation = getFileLocation(".amp", "module/test_v1.amp");
String ampV2Location = getFileLocation(".amp", "module/test_v2.amp");
TFile war = new TFile(warLocation);
List<ModuleDetails> details = this.manager.warHelper.listModules(war);
assertNotNull(details);
assertEquals(details.size(), 0);
this.manager.installModule(ampLocation, warLocation);
details = this.manager.warHelper.listModules(war);
assertNotNull(details);
assertEquals(details.size(), 1);
ModuleDetails aModule = details.get(0);
assertEquals("test", aModule.getId());
assertEquals("1.0", aModule.getModuleVersionNumber().toString());
this.manager.installModule(ampV2Location, warLocation);
details = this.manager.warHelper.listModules(war);
assertNotNull(details);
assertEquals(details.size(), 1);
aModule = details.get(0);
assertEquals("test", aModule.getId());
assertEquals("2.0", aModule.getModuleVersionNumber().toString());
String testAmpDepV2Location = getFileLocation(".amp", "module/dependent_on_test_v2.amp");
String testAmp7 = getFileLocation(".amp", "module/test_v7.amp");
this.manager.installModule(testAmpDepV2Location, warLocation, false, true, false);
this.manager.installModule(testAmp7, warLocation, false, true, false);
details = this.manager.warHelper.listModules(war);
assertNotNull(details);
assertEquals(details.size(), 3);
//Sort them by installation date
Collections.sort(details, new Comparator<ModuleDetails>() {
@Override
public int compare(ModuleDetails a, ModuleDetails b) {
return a.getInstallDate().compareTo(b.getInstallDate());
}
});
ModuleDetails installedModule = details.get(0);
assertEquals("test", installedModule.getId());
assertEquals("2.0", installedModule.getModuleVersionNumber().toString());
installedModule = details.get(1);
assertEquals("org.alfresco.module.test.dependent", installedModule.getId());
assertEquals("2.0", installedModule.getModuleVersionNumber().toString());
installedModule = details.get(2);
assertEquals("forcedtest", installedModule.getId());
assertEquals("1.0", installedModule.getModuleVersionNumber().toString());
}
private String getFileLocation(String extension, String location)
throws IOException
{
File file = TempFileProvider.createTempFile("moduleManagementToolTest-", extension);
InputStream is = this.getClass().getClassLoader().getResourceAsStream(location);
assertNotNull(is);
OutputStream os = new FileOutputStream(file);
FileCopyUtils.copy(is, os);
return file.getPath();
}
private String extractToDir(String extension, String location)
{
File tmpDir = TempFileProvider.getTempDir();
try {
TFile zipFile = new TFile(this.getClass().getClassLoader().getResource(location).getPath());
TFile outDir = new TFile(tmpDir.getAbsolutePath()+"/moduleManagementToolTestDir"+System.currentTimeMillis());
outDir.mkdir();
zipFile.cp_rp(outDir);
TVFS.umount(zipFile);
return outDir.getPath();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void testNoWar() throws Exception
{
String noWar = "noWar";
String ampLocation = getFileLocation(".amp", "module/test_v1.amp");
try
{
this.manager.installModule(ampLocation, noWar,false,false, false);
}
catch (ModuleManagementToolException exception)
{
assertTrue(exception.getMessage().endsWith("does not exist."));
}
try
{
this.manager.installModule(ampLocation, noWar,false,false, true); //backup war
}
catch (ModuleManagementToolException exception)
{
assertTrue(exception.getMessage().endsWith("does not exist."));
}
}
private void checkForFileExistance(String warLocation, List<String> files)
{
for (String file : files)
{
File file0 = new TFile(warLocation + file);
assertTrue("The file/dir " + file + " does not exist in the WAR.", file0.exists());
}
}
private void checkForFileNonExistance(String warLocation, List<String> files)
{
for (String file : files)
{
File file0 = new TFile(warLocation + file);
assertFalse("The file/dir " + file + " does exist in the WAR.", file0.exists());
}
}
private void checkContentsOfFile(String location, String expectedContents)
throws IOException
{
File file = new TFile(location);
assertTrue(file.exists());
BufferedReader reader = null;
try
{
reader = new BufferedReader(new InputStreamReader(new TFileInputStream(file)));
String line = reader.readLine();
assertNotNull(line);
assertEquals(expectedContents, line.trim());
}
finally
{
if (reader != null)
{
try { reader.close(); } catch (Throwable e ) {}
}
}
}
}

View File

@@ -0,0 +1,97 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.module.tool;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.alfresco.repo.admin.registry.RegistryKey;
import org.alfresco.repo.admin.registry.RegistryService;
import org.alfresco.repo.module.ModuleServiceImpl;
import org.alfresco.repo.module.ModuleVersionNumber;
import org.alfresco.service.cmr.module.ModuleDetails;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
/**
* Tests some of ModuleServiceImpl that is not covered by other tests
* @author Gethin James
*/
public class ModuleServiceImplTest
{
ModuleServiceImpl moduleService;
@Before
public void setUp() throws Exception
{
moduleService = new ModuleServiceImpl();
Resource simpleMod = new PathMatchingResourcePatternResolver().getResource("classpath:alfresco/module/simplemodule.properties");
assertNotNull(simpleMod);
RegistryService reg = mock(RegistryService.class);
ApplicationContext applicationContext = mock(ApplicationContext.class);
when(reg.getProperty((RegistryKey) any())).thenAnswer(new Answer<Serializable>()
{
public Serializable answer(InvocationOnMock invocation) throws Throwable
{
RegistryKey key = (RegistryKey) invocation.getArguments()[0];
return new ModuleVersionNumber("1.1");
}
});
doReturn(Arrays.asList("fee", "alfresco-simple-module", "fo")).when(reg).getChildElements((RegistryKey) any());
doReturn(new Resource[] {simpleMod}).when(applicationContext).getResources(anyString());
moduleService.setRegistryService(reg);
moduleService.setApplicationContext(applicationContext);
}
@Test(expected = UnsupportedOperationException.class)
public void testSetApplyToTenants() throws Exception
{
moduleService.setApplyToTenants(false);
}
@Test
public void testGetMissingModules() throws Exception
{
List<ModuleDetails> miss = moduleService.getMissingModules();
assertNotNull(miss);
//There are 3 modules. 2 of which are missing. "alfresco-simple-module" should be found.
assertEquals(2, miss.size());
}
}

View File

@@ -0,0 +1,417 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.module.tool;
import de.schlichtherle.truezip.file.TArchiveDetector;
import de.schlichtherle.truezip.file.TConfig;
import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.fs.archive.zip.ZipDriver;
import de.schlichtherle.truezip.socket.sl.IOPoolLocator;
import org.alfresco.repo.module.ModuleDetailsImpl;
import org.alfresco.repo.module.ModuleVersionNumber;
import org.alfresco.service.cmr.module.ModuleDetails;
import org.alfresco.service.cmr.module.ModuleInstallState;
import org.alfresco.util.TempFileProvider;
import org.alfresco.util.VersionNumber;
import org.junit.Test;
import org.springframework.util.FileCopyUtils;
import java.io.*;
import java.util.List;
import java.util.Properties;
import java.util.jar.Manifest;
import static org.junit.Assert.*;
/**
* Tests the war helper.
*
* @author Gethin James
*/
public class WarHelperImplTest extends WarHelperImpl
{
public WarHelperImplTest()
{
super(new LogOutput()
{
@Override
public void info(Object message)
{
System.out.println(message);
}
});
TConfig config = TConfig.get();
config.setArchiveDetector(new TArchiveDetector("war|amp", new ZipDriver(IOPoolLocator.SINGLETON)));
}
@Test
public void testRegEx()
{
String x = "1";
assertTrue(x.matches(REGEX_NUMBER_OR_DOT));
x = "king";
assertFalse(x.matches(REGEX_NUMBER_OR_DOT));
x = "2.5.a";
assertFalse(x.matches(REGEX_NUMBER_OR_DOT));
x = "1.2.5";
assertTrue(x.matches(REGEX_NUMBER_OR_DOT));
x = "123";
assertTrue(x.matches(REGEX_NUMBER_OR_DOT));
x = "3.4.11";
assertTrue(x.matches(REGEX_NUMBER_OR_DOT));
x = "4.1.1";
assertTrue(x.matches(REGEX_NUMBER_OR_DOT));
x = "4.2.b";
assertFalse(x.matches(REGEX_NUMBER_OR_DOT));
}
@Test
public void testCheckCompatibleVersion()
{
TFile theWar = getFile(".war", "module/test.war"); //Version 4.1.0
ModuleDetails installingModuleDetails = new ModuleDetailsImpl("test_it", new ModuleVersionNumber("9999"), "Test Mod", "Testing module");
installingModuleDetails.setRepoVersionMin(new VersionNumber("10.1"));
try
{
this.checkCompatibleVersion(theWar, installingModuleDetails);
fail(); //should never get here
}
catch (ModuleManagementToolException exception)
{
assertTrue(exception.getMessage().contains("must be installed on a war version equal to or greater than 10.1"));
}
installingModuleDetails.setRepoVersionMin(new VersionNumber("1.1"));
this.checkCompatibleVersion(theWar, installingModuleDetails); //does not throw exception
installingModuleDetails.setRepoVersionMax(new VersionNumber("3.0"));
try
{
this.checkCompatibleVersion(theWar, installingModuleDetails);
fail(); //should never get here
}
catch (ModuleManagementToolException exception)
{
assertTrue(exception.getMessage().contains("cannot be installed on a war version greater than 3.0"));
}
installingModuleDetails.setRepoVersionMax(new VersionNumber("99"));
this.checkCompatibleVersion(theWar, installingModuleDetails); //does not throw exception
installingModuleDetails.setRepoVersionMin(new VersionNumber("4.1.0")); //current war version
installingModuleDetails.setRepoVersionMax(new VersionNumber("4.1.0")); //current war version
this.checkCompatibleVersion(theWar, installingModuleDetails); //does not throw exception
installingModuleDetails.setRepoVersionMin(new VersionNumber("3.4.0")); //current war version
installingModuleDetails.setRepoVersionMax(new VersionNumber("4.1.0")); //current war version
this.checkCompatibleVersion(theWar, installingModuleDetails); //does not throw exception
try
{
installingModuleDetails.setRepoVersionMin(new VersionNumber("3.4.0")); //current war version
installingModuleDetails.setRepoVersionMax(new VersionNumber("4.0.999")); //current war version
this.checkCompatibleVersion(theWar, installingModuleDetails); //does not throw exception
fail("Should not pass as current version is 4.1.0 and the max value is 4.0.999"); //should never get here
}
catch (ModuleManagementToolException exception)
{
assertTrue(exception.getMessage().contains("cannot be installed on a war version greater than 4.0.999"));
}
}
@Test
public void testCheckCompatibleVersionUsingManifest() throws IOException
{
//Now check the compatible versions using the manifest
TFile theWar = getFile(".war", "module/share-3.4.11.war");
ModuleDetails installingModuleDetails = new ModuleDetailsImpl("test_it", new ModuleVersionNumber("9999"), "Test Mod", "Testing module");
installingModuleDetails.setRepoVersionMin(new VersionNumber("10.1"));
try
{
this.checkCompatibleVersionUsingManifest(theWar, installingModuleDetails);
fail(); //should never get here
}
catch (ModuleManagementToolException exception)
{
assertTrue(exception.getMessage().contains("must be installed on a war version equal to or greater than 10.1"));
}
installingModuleDetails.setRepoVersionMin(new VersionNumber("1.1"));
this.checkCompatibleVersionUsingManifest(theWar, installingModuleDetails); //does not throw exception
installingModuleDetails.setRepoVersionMax(new VersionNumber("3.0"));
try
{
this.checkCompatibleVersionUsingManifest(theWar, installingModuleDetails);
fail(); //should never get here
}
catch (ModuleManagementToolException exception)
{
assertTrue(exception.getMessage().contains("cannot be installed on a war version greater than 3.0"));
}
installingModuleDetails.setRepoVersionMax(new VersionNumber("99"));
this.checkCompatibleVersionUsingManifest(theWar, installingModuleDetails); //does not throw exception
installingModuleDetails.setRepoVersionMin(new VersionNumber("3.4.11")); //current war version
installingModuleDetails.setRepoVersionMax(new VersionNumber("3.4.11")); //current war version
this.checkCompatibleVersionUsingManifest(theWar, installingModuleDetails); //does not throw exception
installingModuleDetails.setRepoVersionMin(new VersionNumber("3.4.7")); //current war version
installingModuleDetails.setRepoVersionMax(new VersionNumber("3.4.11")); //current war version
this.checkCompatibleVersionUsingManifest(theWar, installingModuleDetails); //does not throw exception
try
{
installingModuleDetails.setRepoVersionMin(new VersionNumber("3.4.0")); //current war version
installingModuleDetails.setRepoVersionMax(new VersionNumber("3.4.10")); //current war version
this.checkCompatibleVersionUsingManifest(theWar, installingModuleDetails); //does not throw exception
fail("Should not pass as current version is 3.4.11 and the max value is 3.4.10"); //should never get here
}
catch (ModuleManagementToolException exception)
{
assertTrue(exception.getMessage().contains("cannot be installed on a war version greater than 3.4.10"));
}
theWar = getFile(".war", "module/share-4.2.a.war");
installingModuleDetails = new ModuleDetailsImpl("test_it", new ModuleVersionNumber("9999"), "Test Mod", "Testing module");
installingModuleDetails.setRepoVersionMin(new VersionNumber("101.1"));
//this should fail BUT we are using a non-numeric version number so instead it passes without validation
this.checkCompatibleVersionUsingManifest(theWar, installingModuleDetails);
theWar = getFile(".war", "module/alfresco-4.2.a.war");
//this should fail BUT we are using a non-numeric version number so instead it passes without validation
this.checkCompatibleVersionUsingManifest(theWar, installingModuleDetails);
}
@Test
public void testCheckCompatibleEdition()
{
Properties props = dummyModuleProperties();
ModuleDetails installingModuleDetails = new ModuleDetailsImpl(props);
TFile theWar = getFile(".war", "module/test.war"); //Community Edition
//Test for no edition specified
this.checkCompatibleEdition(theWar, installingModuleDetails); //does not throw exception
//Test for invalid edition
props.setProperty(ModuleDetails.PROP_EDITIONS, "CommuniT");
installingModuleDetails = new ModuleDetailsImpl(props);
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[CommuniT]"));
}
props.setProperty(ModuleDetails.PROP_EDITIONS, ("CoMMunity")); //should ignore case
installingModuleDetails = new ModuleDetailsImpl(props);
this.checkCompatibleEdition(theWar, installingModuleDetails); //does not throw exception
props.setProperty(ModuleDetails.PROP_EDITIONS, ("enterprise,community,bob")); //should ignore case
installingModuleDetails = new ModuleDetailsImpl(props);
this.checkCompatibleEdition(theWar, installingModuleDetails); //does not throw exception
props.setProperty(ModuleDetails.PROP_EDITIONS, ("enterprise,Community")); //should ignore case
installingModuleDetails = new ModuleDetailsImpl(props);
this.checkCompatibleVersion(theWar, installingModuleDetails); //does not throw exception
}
@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]"));
}
}
@Test
public void testfindManifest() throws Exception {
//Now check the compatible versions using the manifest
TFile theWar = getFile(".war", "module/share-3.4.11.war");
Manifest manifest = this.findManifest(theWar);
assertNotNull(manifest);
assertEquals("Alfresco Share Enterprise", manifest.getMainAttributes().getValue(MANIFEST_IMPLEMENTATION_TITLE));
assertEquals("3.4.11", manifest.getMainAttributes().getValue(MANIFEST_SPECIFICATION_VERSION));
theWar = getFile(".war", "module/alfresco-4.2.a.war");
manifest = this.findManifest(theWar);
assertNotNull(manifest);
assertEquals("Alfresco Repository Community", manifest.getMainAttributes().getValue(MANIFEST_IMPLEMENTATION_TITLE));
assertEquals("4.2.a", manifest.getMainAttributes().getValue(MANIFEST_SPECIFICATION_VERSION));
}
@Test
public void testListModules() throws Exception
{
TFile theWar = getFile(".war", "module/test.war");
List<ModuleDetails> details = this.listModules(theWar);
assertNotNull(details);
assertEquals(details.size(), 0);
theWar = getFile(".war", "module/share-4.2.a.war");
details = this.listModules(theWar);
assertNotNull(details);
assertEquals(details.size(), 1);
ModuleDetails aModule = details.get(0);
assertEquals("alfresco-mm-share", aModule.getId());
assertEquals("0.1.5.6", aModule.getModuleVersionNumber().toString());
assertEquals(ModuleInstallState.INSTALLED, aModule.getInstallState());
}
private Properties dummyModuleProperties() {
Properties props = new Properties();
props.setProperty(ModuleDetails.PROP_ID, "TestComp");
props.setProperty(ModuleDetails.PROP_VERSION, "9999");
props.setProperty(ModuleDetails.PROP_TITLE, "Test for Compatiblity");
props.setProperty(ModuleDetails.PROP_DESCRIPTION, "Test for Compatible Editions");
return props;
}
@Test
public void testNoVersionProperties()
{
TFile theWar = getFile(".war", "module/empty.war");
ModuleDetails installingModuleDetails = new ModuleDetailsImpl("test_it", new ModuleVersionNumber("9999"), "Test Mod", "Testing module");
installingModuleDetails.setRepoVersionMin(new VersionNumber("10.1"));
this.checkCompatibleVersion(theWar, installingModuleDetails); //does not throw exception
this.checkCompatibleEdition(theWar, installingModuleDetails); //does not throw exception
}
/**
* Tests to see if the war is a share war.
*/
@Test
public void testIsShareWar()
{
TFile theWar = getFile(".war", "module/test.war"); //Version 4.1.0
assertFalse(this.isShareWar(theWar));
theWar = getFile(".war", "module/empty.war");
assertFalse(this.isShareWar(theWar));
theWar = getFile(".war", "module/alfresco-4.2.a.war");
assertFalse(this.isShareWar(theWar));
theWar = getFile(".war", "module/share-4.2.a.war");
assertTrue(this.isShareWar(theWar));
}
private TFile getFile(String extension, String location)
{
File file = TempFileProvider.createTempFile("moduleManagementToolTest-", extension);
InputStream is = this.getClass().getClassLoader().getResourceAsStream(location);
assertNotNull(is);
OutputStream os;
try
{
os = new FileOutputStream(file);
FileCopyUtils.copy(is, os);
}
catch (IOException error)
{
error.printStackTrace();
}
return new TFile(file);
}
}

View File

@@ -0,0 +1,27 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==== Beginning of Alfresco required/optional properties ====== #
# NB: These properties are filtered at build time by Maven, single
# sourcing from POM properties
module.id=alfresco-simple-module
#module.aliases=myModule-123, my-module
module.title=Alfresco JAR Module Project
module.description=UNSUPPORTED experiment
module.version=1.0.0-SNAPSHOT
# The following optional properties can be used to prevent the module from being added
# to inappropriate versions of the WAR file.
module.repo.version.min=2.0

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.