From a6fb91693a30535d94953c93094537059b0481cc Mon Sep 17 00:00:00 2001 From: Gethin James Date: Tue, 16 Oct 2012 15:18:28 +0000 Subject: [PATCH] Following ALF-14738, adding functionality to use the manifest to check the versions / editions git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@42689 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/repo/module/tool/WarHelper.java | 10 ++ .../repo/module/tool/WarHelperImpl.java | 140 ++++++++++++++- .../repo/module/tool/WarHelperImplTest.java | 163 ++++++++++++++++-- 3 files changed, 293 insertions(+), 20 deletions(-) diff --git a/source/java/org/alfresco/repo/module/tool/WarHelper.java b/source/java/org/alfresco/repo/module/tool/WarHelper.java index 058fcdccc6..2b02946a93 100644 --- a/source/java/org/alfresco/repo/module/tool/WarHelper.java +++ b/source/java/org/alfresco/repo/module/tool/WarHelper.java @@ -44,5 +44,15 @@ public interface WarHelper * @param installingModuleDetails */ 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 + * @return boolean - true if it is a share war + */ + public boolean isShareWar(TFile war); } diff --git a/source/java/org/alfresco/repo/module/tool/WarHelperImpl.java b/source/java/org/alfresco/repo/module/tool/WarHelperImpl.java index 1b5f7d8db0..fca44f013b 100644 --- a/source/java/org/alfresco/repo/module/tool/WarHelperImpl.java +++ b/source/java/org/alfresco/repo/module/tool/WarHelperImpl.java @@ -5,11 +5,15 @@ 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.JarFile; +import java.util.jar.Manifest; 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 org.apache.commons.lang.StringUtils; import de.schlichtherle.truezip.file.TFile; import de.schlichtherle.truezip.file.TFileInputStream; @@ -24,6 +28,15 @@ public class WarHelperImpl implements WarHelper { public static final String VERSION_PROPERTIES = "/WEB-INF/classes/alfresco/version.properties"; + + //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"; + private LogOutput log = null; @@ -42,18 +55,84 @@ public class WarHelperImpl implements WarHelper { Properties warVers = loadProperties(propsFile); VersionNumber warVersion = new VersionNumber(warVers.getProperty("version.major")+"."+warVers.getProperty("version.minor")+"."+warVers.getProperty("version.revision")); - if(warVersion.compareTo(installingModuleDetails.getRepoVersionMin())==-1) { - throw new ModuleManagementToolException("The module ("+installingModuleDetails.getTitle()+") must be installed on a repo version greater than "+installingModuleDetails.getRepoVersionMin()); - } - if(warVersion.compareTo(installingModuleDetails.getRepoVersionMax())==1) { - throw new ModuleManagementToolException("The module ("+installingModuleDetails.getTitle()+") cannot be installed on a repo version greater than "+installingModuleDetails.getRepoVersionMax()); - } + checkVersions(warVersion, installingModuleDetails); } else { - log.info("No valid version found, is this a share war?"); + 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 + * @param installingModuleDetails + */ + protected void checkCompatibleVersionUsingManifest(TFile war, ModuleDetails installingModuleDetails) + { + String version = findManifestArtibute(war, MANIFEST_SPECIFICATION_VERSION); + if (StringUtils.isNotBlank(version)) + { + if (StringUtils.containsOnly(version, "0123456789.")) { + 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("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); + } + } + + } + } + + /** + * 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 { + try + { + JarFile warAsJar = new JarFile(war); + Manifest manifest = warAsJar.getManifest(); + Attributes attribs = manifest.getMainAttributes(); + return attribs.getValue(attributeName); + } + catch (IOException e) + { + throw new ModuleManagementToolException("Unabled to read a manifest for the war file: "+ war); + } + } + + + /** + * Compares the version information with the module details to see if their valid. If they are invalid then it throws an exception. + * @param warVersion + * @param installingModuleDetails + * @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 greater than "+installingModuleDetails.getRepoVersionMin()); + } + if(warVersion.compareTo(installingModuleDetails.getRepoVersionMax())==1) { + throw new ModuleManagementToolException("The module ("+installingModuleDetails.getTitle()+") cannot be installed on a war version greater than "+installingModuleDetails.getRepoVersionMax()); + } + } @Override public void checkCompatibleEdition(TFile war, ModuleDetails installingModuleDetails) @@ -84,6 +163,38 @@ public class WarHelperImpl implements WarHelper } } + /** + * 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 + * @param installingModuleDetails + */ + public void checkCompatibleEditionUsingManifest(TFile war, ModuleDetails installingModuleDetails) + { + List installableEditions = installingModuleDetails.getEditions(); + + if (installableEditions != null && installableEditions.size() > 0) { + + String warEdition = findManifestArtibute(war, MANIFEST_IMPLEMENTATION_TITLE); + if (StringUtils.isNotBlank(warEdition)) + { + 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("No valid editions found in the manifest. Is this war prior to 3.4.11, 4.1.1 and Community 4.2 ?"); + } + } + } + @Override public void checkModuleDependencies(TFile war, ModuleDetails installingModuleDetails) { @@ -132,6 +243,21 @@ public class WarHelperImpl implements WarHelper 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 + } + /** * Gets the module details for the specified module from the war. * @param war a valid war file or exploded directory from a war diff --git a/source/java/org/alfresco/repo/module/tool/WarHelperImplTest.java b/source/java/org/alfresco/repo/module/tool/WarHelperImplTest.java index 67533c9260..40c345f8d9 100644 --- a/source/java/org/alfresco/repo/module/tool/WarHelperImplTest.java +++ b/source/java/org/alfresco/repo/module/tool/WarHelperImplTest.java @@ -1,8 +1,6 @@ package org.alfresco.repo.module.tool; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; import java.io.File; import java.io.FileOutputStream; @@ -16,9 +14,8 @@ import org.alfresco.service.cmr.module.ModuleDetails; import org.alfresco.util.TempFileProvider; import org.alfresco.util.VersionNumber; import org.junit.Test; -import org.springframework.util.FileCopyUtils; - import de.schlichtherle.truezip.file.TFile; +import org.springframework.util.FileCopyUtils; /** * Tests the war helper. @@ -54,7 +51,7 @@ public class WarHelperImplTest extends WarHelperImpl } catch (ModuleManagementToolException exception) { - assertTrue(exception.getMessage().endsWith("must be installed on a repo version greater than 10.1")); + assertTrue(exception.getMessage().endsWith("must be installed on a war version greater than 10.1")); } installingModuleDetails.setRepoVersionMin(new VersionNumber("1.1")); @@ -68,7 +65,7 @@ public class WarHelperImplTest extends WarHelperImpl } catch (ModuleManagementToolException exception) { - assertTrue(exception.getMessage().endsWith("cannot be installed on a repo version greater than 3.0")); + assertTrue(exception.getMessage().endsWith("cannot be installed on a war version greater than 3.0")); } installingModuleDetails.setRepoVersionMax(new VersionNumber("99")); @@ -91,18 +88,82 @@ public class WarHelperImplTest extends WarHelperImpl } catch (ModuleManagementToolException exception) { - assertTrue(exception.getMessage().endsWith("cannot be installed on a repo version greater than 4.0.999")); + assertTrue(exception.getMessage().endsWith("cannot be installed on a war version greater than 4.0.999")); } } + + @Test + public void testCheckCompatibleVersionUsingManifest() + { + //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 VersionNumber("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().endsWith("must be installed on a war version 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().endsWith("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().endsWith("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 VersionNumber("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 = 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"); + Properties props = dummyModuleProperties(); ModuleDetails installingModuleDetails = new ModuleDetailsImpl(props); TFile theWar = getFile(".war", "module/test.war"); //Community Edition @@ -136,6 +197,59 @@ public class WarHelperImplTest extends WarHelperImpl this.checkCompatibleVersion(theWar, installingModuleDetails); //does not throw exception } + @Test + public void testCheckCompatibleEditionUsingManifest() + { + 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); + } + + + 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() { @@ -147,6 +261,29 @@ public class WarHelperImplTest extends WarHelperImpl this.checkCompatibleEdition(theWar, installingModuleDetails); //does not throw exception } + + /** + * Tests to see if the war is a share war. + * @throws Exception + */ + @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);