Merged DEV\EXTENSIONS to HEAD

svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/DEV/EXTENSIONS@4832 svn://svn.alfresco.com:3691/alfresco/BRANCHES/DEV/EXTENSIONS@4833 .
   svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/DEV/EXTENSIONS@4862 svn://svn.alfresco.com:3691/alfresco/BRANCHES/DEV/EXTENSIONS@4863 .
   svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/DEV/EXTENSIONS@4866 svn://svn.alfresco.com:3691/alfresco/BRANCHES/DEV/EXTENSIONS@4867 .
   svn rm --force root\projects\repository\source\java\org\alfresco\repo\extension
   svn rm --force root\projects\repository\source\java\org\alfresco\service\cmr\extension


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4954 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2007-01-29 14:02:14 +00:00
parent 4287565b19
commit 05d2cd1b28
9 changed files with 694 additions and 0 deletions

View File

@@ -43,5 +43,9 @@
-->
<import resource="classpath*:alfresco/extension/*-context.xml"/>
<import resource="classpath*:alfresco/extension/dev-context.xml" />
<!-- Import of installed modules -->
<import resource="classpath*:alfresco/module/*/module-context.xml"/>
<import resource="classpath*:alfresco/module/*/module-uninstall-context.xml"/>
</beans>

View File

@@ -0,0 +1,430 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.
*/
package org.alfresco.repo.module;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.util.VersionNumber;
import org.apache.log4j.Logger;
import de.schlichtherle.io.DefaultRaesZipDetector;
import de.schlichtherle.io.File;
import de.schlichtherle.io.FileInputStream;
import de.schlichtherle.io.FileOutputStream;
import de.schlichtherle.io.ZipControllerException;
import de.schlichtherle.io.ZipDetector;
import de.schlichtherle.io.ZipWarningException;
/**
* @author Roy Wetherall
*/
public class ModuleManagementTool
{
public static Logger logger = Logger.getLogger("org.alfresco.repo.extension.ModuleManagementTool");
private static final String DEFAULT_FILE_MAPPING_PROPERTIES = "org/alfresco/repo/module/default-file-mapping.properties";
private static final String MODULE_DIR = "/WEB-INF/classes/alfresco/module";
private static final String DELIMITER = ":";
private static final String PROP_ID = "module.id";
private static final String PROP_TITLE = "module.title";
private static final String PROP_DESCRIPTION = "module.description";
private static final String PROP_VERSION = "module.version";
private static final String MOD_ADD_FILE = "add";
private static final String MOD_UPDATE_FILE = "update";
private static final String MOD_MK_DIR = "mkdir";
private static final String OP_INSTALL = "install";
private ZipDetector defaultDetector;
private Properties fileMappingProperties;
private boolean verbose = false;
public ModuleManagementTool()
{
// Create the default zip detector
this.defaultDetector = new DefaultRaesZipDetector("amp|war");
// Load the default file mapping properties
this.fileMappingProperties = new Properties();
InputStream is = this.getClass().getClassLoader().getResourceAsStream(DEFAULT_FILE_MAPPING_PROPERTIES);
try
{
this.fileMappingProperties.load(is);
}
catch (IOException exception)
{
throw new ModuleManagementToolException("Unable to load default extension file mapping properties.", exception);
}
}
public boolean isVerbose()
{
return verbose;
}
public void setVerbose(boolean verbose)
{
this.verbose = verbose;
}
public void installModule(String ampFileLocation, String warFileLocation)
{
try
{
// Load the extension properties
File installingPropertiesFile = new File(ampFileLocation + "/module.properties", this.defaultDetector);
if (installingPropertiesFile.exists() == false)
{
throw new ModuleManagementToolException("Extension properties are not present in the AMP. Check that a valid module.properties file is present.");
}
Properties installingProperties = new Properties();
installingProperties.load(new FileInputStream(installingPropertiesFile));
// Get the intalling extension version
String installingVersionString = installingProperties.getProperty(PROP_VERSION);
if (installingVersionString == null || installingVersionString.length() == 0)
{
throw new ModuleManagementToolException("The version number has not been specified in the module properties found in the AMP.");
}
VersionNumber installingVersion = new VersionNumber(installingVersionString);
// Get the installed directory
File installDir = getInstalledDir(warFileLocation);
// Look for a previously installed version of this extension
File installedExtensionPropertiesFile = new File(installDir.getPath() + "/" + getModuleDetailsFileName(installingProperties.getProperty(PROP_ID)), this.defaultDetector);
if (installedExtensionPropertiesFile.exists() == true)
{
Properties installedExtensionProperties = new Properties();
InputStream is = new FileInputStream(installedExtensionPropertiesFile);
installedExtensionProperties.load(is);
// Get the installed version
VersionNumber installedVersion = new VersionNumber(installedExtensionProperties.getProperty(PROP_VERSION));
int compareValue = installedVersion.compareTo(installingVersion);
if (compareValue == -1)
{
// Trying to update the extension, old files need to cleaned before we proceed
cleanWAR(warFileLocation, installedExtensionProperties);
}
else if (compareValue == 0)
{
// Trying to install the same extension version again
verboseMessage("WARNING: This version of this module is already installed in the WAR");
throw new ModuleManagementToolException("This version of this module is alreay installed. Use the 'force' parameter if you want to overwrite the current installation.");
}
else if (compareValue == 1)
{
// Trying to install an earlier version of the extension
verboseMessage("WARNING: A later version of this module is already installed in the WAR");
throw new ModuleManagementToolException("An earlier version of this module is already installed. You must first unistall the current version before installing this version of the module.");
}
}
// TODO check for any additional file mapping propeties supplied in the AEP file
// Copy the files from the AEP file into the WAR file
Map<String, String> modifications = new HashMap<String, String>(50);
for (Map.Entry<Object, Object> entry : this.fileMappingProperties.entrySet())
{
modifications.putAll(copyToWar(ampFileLocation, warFileLocation, (String)entry.getKey(), (String)entry.getValue()));
}
// Copy the properties file into the war
if (installedExtensionPropertiesFile.exists() == false)
{
installedExtensionPropertiesFile.createNewFile();
}
InputStream is = new FileInputStream(installingPropertiesFile);
try
{
installedExtensionPropertiesFile.catFrom(is);
}
finally
{
is.close();
}
// Create and add the modifications file to the war
writeModificationToFile(installDir.getPath() + "/" + getModuleModificationFileName(installingProperties.getProperty(PROP_ID)), modifications);
// Update the zip file's
File.update();
}
catch (ZipWarningException ignore)
{
// Only instances of the class ZipWarningException exist in the chain of
// exceptions. We choose to ignore this.
}
catch (ZipControllerException exception)
{
// At least one exception occured which is not just a ZipWarningException.
// This is a severe situation that needs to be handled.
throw new ModuleManagementToolException("A Zip error was encountered during deployment of the AEP into the WAR", exception);
}
catch (IOException exception)
{
throw new ModuleManagementToolException("An IO error was encountered during deployment of the AEP into the WAR", exception);
}
}
private void cleanWAR(String warFileLocation, Properties installedExtensionProperties)
{
// Get the currently installed modifications
Map<String, String> modifications = readModificationsFromFile(warFileLocation + "/" + getModuleModificationFileName(installedExtensionProperties.getProperty(PROP_ID)));
for (Map.Entry<String, String> modification : modifications.entrySet())
{
String modType = modification.getValue();
if (MOD_ADD_FILE.equals(modType) == true)
{
// Remove file
}
else if (MOD_UPDATE_FILE.equals(modType) == true)
{
// Remove file
// Replace with back-up
}
else if (MOD_MK_DIR.equals(modType) == true)
{
// Add to list of dir's to remove at the end
}
}
}
private Map<String, String> copyToWar(String aepFileLocation, String warFileLocation, String sourceDir, String destinationDir)
throws IOException
{
Map<String, String> result = new HashMap<String, String>(10);
String sourceLocation = aepFileLocation + sourceDir;
File aepConfig = new File(sourceLocation, this.defaultDetector);
for (java.io.File sourceChild : aepConfig.listFiles())
{
String destinationFileLocation = warFileLocation + destinationDir + "/" + sourceChild.getName();
File destinationChild = new File(destinationFileLocation, this.defaultDetector);
if (sourceChild.isFile() == true)
{
boolean createFile = false;
if (destinationChild.exists() == false)
{
destinationChild.createNewFile();
createFile = true;
}
FileInputStream fis = new FileInputStream(sourceChild);
try
{
destinationChild.catFrom(fis);
}
finally
{
fis.close();
}
if (createFile == true)
{
result.put(destinationDir + "/" + sourceChild.getName(), MOD_ADD_FILE);
this.verboseMessage("File added: " + destinationDir + "/" + sourceChild.getName());
}
else
{
result.put(destinationDir + "/" + sourceChild.getName(), MOD_UPDATE_FILE);
this.verboseMessage("File updated:" + destinationDir + "/" + sourceChild.getName());
}
}
else
{
boolean mkdir = false;
if (destinationChild.exists() == false)
{
destinationChild.mkdir();
mkdir = true;
}
Map<String, String> subResult = copyToWar(aepFileLocation, warFileLocation, sourceDir + "/" + sourceChild.getName(),
destinationDir + "/" + sourceChild.getName());
result.putAll(subResult);
if (mkdir == true)
{
result.put(destinationDir + "/" + sourceChild.getName(), MOD_MK_DIR);
this.verboseMessage("Directory added: " + destinationDir + "/" + sourceChild.getName());
}
}
}
return result;
}
private File getInstalledDir(String warFileLocation)
{
// Check for the installed directory in the WAR file
File installedDir = new File(warFileLocation + MODULE_DIR, this.defaultDetector);
if (installedDir.exists() == false)
{
installedDir.mkdir();
}
return installedDir;
}
public void disableModule(String moduleId, String warLocation)
{
System.out.println("Currently unsupported ...");
}
public void enableModule(String moduleId, String warLocation)
{
System.out.println("Currently unsupported ...");
}
public void uninstallModule(String moduleId, String warLocation)
{
System.out.println("Currently unsupported ...");
}
public void listModules(String warLocation)
{
System.out.println("Currently unsupported ...");
}
private void verboseMessage(String message)
{
if (this.verbose == true)
{
System.out.println(message);
}
}
private void writeModificationToFile(String fileLocation, Map<String, String> modifications)
throws IOException
{
File file = new File(fileLocation, this.defaultDetector);
if (file.exists() == false)
{
file.createNewFile();
}
FileOutputStream os = new FileOutputStream(file);
try
{
for (Map.Entry<String, String> mod : modifications.entrySet())
{
String output = mod.getValue() + DELIMITER + mod.getKey() + "\n";
os.write(output.getBytes());
}
}
finally
{
os.close();
}
}
private Map<String, String> readModificationsFromFile(String fileLocation)
{
Map<String, String> modifications = new HashMap<String, String>(50);
File file = new File(fileLocation, this.defaultDetector);
try
{
BufferedReader reader = new BufferedReader(new FileReader(file));
try
{
String line = reader.readLine();
while (line != null)
{
line = reader.readLine();
String[] modification = line.split(DELIMITER);
modifications.put(modification[1], modification[0]);
}
}
finally
{
reader.close();
}
}
catch(FileNotFoundException exception)
{
throw new ModuleManagementToolException("The module file install file '" + fileLocation + "' does not exist");
}
catch(IOException exception)
{
throw new ModuleManagementToolException("Error whilst reading file '" + fileLocation);
}
return modifications;
}
private String getModuleDetailsFileName(String moduleId)
{
return "module-" + moduleId + ".install";
}
private String getModuleModificationFileName(String moduleId)
{
return "module-" + moduleId + "-modifications.install";
}
/**
* @param args
*/
public static void main(String[] args)
{
if (args.length >= 1)
{
ModuleManagementTool manager = new ModuleManagementTool();
String operation = args[0];
if (operation.equals(OP_INSTALL) == true && args.length >= 3)
{
String aepFileLocation = args[1];
String warFileLocation = args[2];
manager.installModule(aepFileLocation, warFileLocation);
}
else
{
outputUsage();
}
}
else
{
outputUsage();
}
}
private static void outputUsage()
{
System.out.println("output useage ...");
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.
*/
package org.alfresco.repo.module;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Module Management Tool Exception class
*
* @author Roy Wetherall
*/
public class ModuleManagementToolException extends AlfrescoRuntimeException
{
/**
* Serial version UID
*/
private static final long serialVersionUID = -4329693103965834085L;
public ModuleManagementToolException(String msgId)
{
super(msgId);
}
public ModuleManagementToolException(String msgId, Object[] msgParams)
{
super(msgId, msgParams);
}
public ModuleManagementToolException(String msgId, Object[] msgParams, Throwable cause)
{
super(msgId, msgParams, cause);
}
public ModuleManagementToolException(String msgId, Throwable cause)
{
super(msgId, cause);
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.
*/
package org.alfresco.repo.module;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
import org.springframework.util.FileCopyUtils;
import de.schlichtherle.io.DefaultRaesZipDetector;
import de.schlichtherle.io.FileOutputStream;
import de.schlichtherle.io.ZipDetector;
/**
* @author Roy Wetherall
*/
public class ModuleManagementToolTest extends TestCase
{
private ModuleManagementTool manager = new ModuleManagementTool();
ZipDetector defaultDetector = new DefaultRaesZipDetector("amp|war");
public void testBasicInstall()
throws Exception
{
manager.setVerbose(true);
String warLocation = getFileLocation(".war", "module/test.war");
String ampLocation = getFileLocation(".amp", "module/test.amp");
System.out.println(warLocation);
// 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/module-test.install");
files.add("/WEB-INF/classes/alfresco/module/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");
checkForFileExistance(warLocation, files);
// Try and install same version
try
{
this.manager.installModule(ampLocation, warLocation);
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
// TODO
// Try and install and earlier version
// TODO
}
private String getFileLocation(String extension, String location)
throws IOException
{
File file = File.createTempFile("moduleManagementToolTest-", extension);
InputStream is = this.getClass().getClassLoader().getResourceAsStream(location);
OutputStream os = new FileOutputStream(file);
FileCopyUtils.copy(is, os);
return file.getPath();
}
private void checkForFileExistance(String warLocation, List<String> files)
{
for (String file : files)
{
File file0 = new de.schlichtherle.io.File(warLocation + file, this.defaultDetector);
assertTrue("The file/dir " + file + " does not exist in the WAR.", file0.exists());
}
}
}

View File

@@ -0,0 +1,8 @@
# The default AEP => 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

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.
*/
package org.alfresco.service.cmr.module;
import org.alfresco.util.VersionNumber;
/**
* Module details, contains the details of an installed alfresco
* module.
*
* @author Roy Wetherall
*/
public class ModuleDetails
{
private String id;
private VersionNumber version;
private String title;
private String description;
public ModuleDetails(String id, VersionNumber version, String title, String description)
{
this.id = id;
this.version = version;
this.title = title;
}
public String getId()
{
return id;
}
public VersionNumber getVersion()
{
return version;
}
public String getTitle()
{
return title;
}
public String getDescription()
{
return description;
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.
*/
package org.alfresco.service.cmr.module;
import java.util.List;
/**
* Module service. Provides information about the currently installed alfresco
* modules.
*
* @author Roy Wetherall
*/
public interface ModuleService
{
public ModuleDetails getModule(String moduleId);
public List<ModuleDetails> getAllModules();
}

Binary file not shown.

Binary file not shown.