/* * JBoss, Home of Professional Open Source. * Copyright 2016, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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 2.1 of * the License, or (at your option) any later version. * * This software 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 this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.test.integration.management.cli; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; import org.apache.commons.io.FileUtils; import org.jboss.as.test.integration.management.base.AbstractCliTestBase; import org.jboss.as.test.shared.TestSuiteEnvironment; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.jboss.shrinkwrap.impl.base.exporter.zip.ZipExporterImpl; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NodeList; import org.wildfly.core.testrunner.WildflyTestRunner; import org.xnio.IoUtils; /** * This tests 'module add/remove' CLI command. * * @author Ivo Studensky */ @RunWith(WildflyTestRunner.class) public class ModuleTestCase extends AbstractCliTestBase { private static final String MODULE_NAME = "org.jboss.test.cli.climoduletest"; private static File jarFile; private static File jarFile2; private static File customModulesDirectory; @BeforeClass public static void beforeClass() throws Exception { final JavaArchive jar = ShrinkWrap.create(JavaArchive.class, "Dummy.jar"); jar.addClass(ModuleTestCase.class); jarFile = new File(TestSuiteEnvironment.getTmpDir() + File.separator + "Dummy.jar"); jarFile2 = new File(TestSuiteEnvironment.getTmpDir() + File.separator + "Dummy2.jar"); new ZipExporterImpl(jar).exportTo(jarFile, true); new ZipExporterImpl(jar).exportTo(jarFile2, true); // Create an empty directory customModulesDirectory = new File(TestSuiteEnvironment.getTmpDir(), System.currentTimeMillis() + "-mymodules"); customModulesDirectory.mkdir(); AbstractCliTestBase.initCLI(); } @AfterClass public static void afterClass() throws Exception { jarFile.delete(); jarFile2.delete(); FileUtils.deleteDirectory(customModulesDirectory); AbstractCliTestBase.closeCLI(); } @Test public void userModulesPath() throws Exception { testAddRemove("main", true, true, true, false, false, false); } @Test public void addRemoveModuleWithResources() throws Exception { testAddRemove("main", false, true, false, false, false, false); } @Test public void addRemoveModuleWithResourcesAndDependencies() throws Exception { testAddRemove("main", false, true, false, false, true, false); } @Test public void addRemoveModuleWithResourcesAndProperties() throws Exception { testAddRemove("main", false, true, false, true, false, false); } @Test public void addRemoveModuleWithResourcesAndMainClass() throws Exception { testAddRemove("main", false, true, false, false, false, true); } @Test public void addRemoveModuleWithResourcesAndPropertiesAndDependencies() throws Exception { testAddRemove("main", false, true, false, true, true, false); } @Test public void addRemoveModuleWithResourcesAndPropertiesAndMainClass() throws Exception { testAddRemove("main", false, true, false, true, false, true); } @Test public void addRemoveModuleWithResourcesAndDependenciesAndMainClass() throws Exception { testAddRemove("main", false, true, false, false, true, true); } @Test public void addRemoveModuleWithResourcesAndPropertiesAndDependenciesAndMainClass() throws Exception { testAddRemove("main", false, true, false, true, true, true); } @Test public void addRemoveModuleWithAbsoluteResources() throws Exception { testAddRemove("main", false, false, true, false, false, false); } @Test public void addRemoveModuleWithBothTypeResources() throws Exception { testAddRemove("main", false, true, true, false, false, false); } @Test public void addRemoveModuleNonDefaultSloteWithResources() throws Exception { testAddRemove("2.0", false, true, false, false, false, false); } @Test public void addRemoveModuleNonDefaultSloteWithAbsoluteResources() throws Exception { testAddRemove("2.0", false, false, true, false, false, false); } @Test public void addRemoveModuleNonDefaultSloteWithBothTypeResources() throws Exception { testAddRemove("2.0", false, true, true, false, false, false); } // test for WFLY-1896 @Test public void addRemoveModuleMetaInf() throws Exception { final String slot = "main"; testModuleAndSlotExist(false, slot, getModulePath()); testAdd(slot, false, true, true, false, false, false); testModuleFiles(slot, getModulePath(), true, true, false, false, false); // create a META-INF directory inside the module final File metaInfDir = new File(getModulePath(), MODULE_NAME.replace('.', File.separatorChar) + File.separator + "main" + File.separator + "META-INF"); if (!metaInfDir.mkdirs()) { fail("Could not create " + metaInfDir); } PrintWriter out = null; try { out = new PrintWriter(new File(metaInfDir, "version.txt")); out.println("main"); } finally { IoUtils.safeClose(out); } testRemove(slot, false); testModuleAndSlotExist(false, slot, getModulePath()); } @Test public void addModuleWithWhitespacesInDependencies() throws Exception { // should trim the surrounding whitespaces try { cli.sendLine("module add --name=" + MODULE_NAME + " --resources=" + jarFile.getAbsolutePath() + " --dependencies=[ org.jboss.logging ]" + " --export-dependencies=[ org.jboss.logmanager ]"); testModuleFiles("main", getModulePath(), false, false, false, true, false); } finally { testRemove("main", false); } } private void testAddRemove(String slotName, boolean addModuleRootDir, boolean addResources, boolean addAbsoluteResources, boolean addPropeties, boolean addDependencies, boolean addMainClass) throws Exception { File modulePath = addModuleRootDir ? customModulesDirectory : getModulePath(); testModuleAndSlotExist(false, slotName, modulePath); testAdd(slotName, addModuleRootDir, addResources, addAbsoluteResources, addPropeties, addDependencies, addMainClass); testModuleFiles(slotName, modulePath, addResources, addAbsoluteResources, addPropeties, addDependencies, addMainClass); testRemove(slotName, addModuleRootDir); testModuleAndSlotExist(false, slotName, modulePath); } private void testAdd(String slotName, boolean addModuleRootDir, boolean addResources, boolean addAbsoluteResources, boolean addProperties, boolean addDependencies, boolean addMainClass) throws Exception { // create a module cli.sendLine("module add --name=" + MODULE_NAME + ("main".equals(slotName) ? "" : " --slot=" + slotName) + (addResources ? " --resources=" + jarFile.getAbsolutePath() : "") + (addAbsoluteResources ? " --absolute-resources= " + jarFile2.getAbsolutePath() : "") + (addModuleRootDir ? " --module-root-dir=" + customModulesDirectory.getAbsolutePath() : "") + (addProperties ? " --properties=bat=man" : "") + (addDependencies ? " --dependencies=org.jboss.logging" : "") + (addDependencies ? " --export-dependencies=org.jboss.logmanager" : "") + (addMainClass ? " --main-class=org.jboss.Test" : "")); } private void testRemove(String slotName, boolean addModuleRootDir) throws Exception { // remove the module cli.sendLine("module remove --name=" + MODULE_NAME + ("main".equals(slotName) ? "" : " --slot=" + slotName) + ( addModuleRootDir ? " --module-root-dir=" + customModulesDirectory.getAbsolutePath() : "")); } private void testModuleExist(boolean ifExists, File modulesPath) throws Exception{ File testModuleRoot = new File(modulesPath, MODULE_NAME.replace('.', File.separatorChar)); assertTrue("Invalid state of module directory: " + testModuleRoot.getAbsolutePath() + (ifExists ? " does not exist" : " exists"), ifExists == testModuleRoot.exists()); } private void testSlotExist(boolean ifExists, String slotName, File modulesPath) throws Exception{ File testModuleRoot = new File(modulesPath, MODULE_NAME.replace('.', File.separatorChar)); File slot = new File(testModuleRoot, slotName); assertTrue("Invalid state of slot directory: " + slot.getAbsolutePath() + (ifExists ? " does not exist" : " exists"), ifExists == slot.exists()); } private void testModuleAndSlotExist(boolean ifExists, String slotName, File modulesPath) throws Exception { testModuleExist(ifExists, modulesPath); testSlotExist(ifExists, slotName, modulesPath); } private void testModuleFiles(String slotName, File modulesPath, boolean verifyResources, boolean verifyAbsoluteResources, boolean verifyProperties, boolean verifyDependencies, boolean verifyMainClass) throws Exception { testModuleAndSlotExist(true, slotName, modulesPath); checkModuleFiles(slotName, modulesPath, verifyResources); checkModuleXml(slotName, modulesPath, verifyResources, verifyAbsoluteResources, verifyProperties, verifyDependencies, verifyMainClass); } private File getModulePath() { String modulePath = TestSuiteEnvironment.getSystemProperty("module.path", null); if (modulePath == null) { String jbossHome = TestSuiteEnvironment.getSystemProperty("jboss.dist", null); if (jbossHome == null) { throw new IllegalStateException( "Neither -Dmodule.path nor -Djboss.home were set"); } modulePath = jbossHome + File.separatorChar + "modules"; } else { modulePath = modulePath.split(File.pathSeparator)[0]; } File moduleDir = new File(modulePath); if (!moduleDir.exists()) { throw new IllegalStateException( "Determined module path does not exist"); } if (!moduleDir.isDirectory()) { throw new IllegalStateException( "Determined module path is not a dir"); } return moduleDir; } private void checkModuleFiles(String slot, File modulesPath, boolean checkResources) { File testModuleRoot = new File(modulesPath, MODULE_NAME.replace('.', File.separatorChar)); File dir = new File(testModuleRoot, slot); if (!dir.isDirectory()) { throw new IllegalArgumentException("Cannot list files, " + dir.getAbsolutePath() + " is not directory"); } // check that there is no directory File[] dirs = dir.listFiles(File::isDirectory); assertTrue("Module shouldn't contain any directory, but it contains " + dirs.length + " + directories.", dirs.length == 0); // check that module contains only module.xml and expected files List<String> files = Arrays.asList(dir.list((directory, name) -> { File f = new File(directory, name); return !f.isDirectory(); })); assertTrue("Module should contain module.xml", files.contains("module.xml")); if(checkResources) { assertTrue("Module should contain module.xml", files.contains("Dummy.jar")); assertTrue("Module should contain only module.xml and Dummy.jar, but there is more files: " + files.toString(), files.size() == 2); } } private void checkModuleXml(String slotName, File modulesPath, boolean checkResources, boolean checkAbsoluteResources, boolean checkProperties, boolean checkDependencies, boolean checkMainClass) throws Exception { File testModuleRoot = new File(modulesPath, MODULE_NAME.replace('.', File.separatorChar)); File slot = new File(testModuleRoot, slotName); File moduleXml = new File(slot, "module.xml"); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(moduleXml); XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); // check resource-root XPathExpression pathAttrExpr = xpath.compile("/module/resources/resource-root/@path"); NodeList nl = (NodeList) pathAttrExpr.evaluate(doc, XPathConstants.NODESET); List<String> paths = new ArrayList<>(2); for (int i = 0; i < nl.getLength(); i++) { paths.add(nl.item(i).getNodeValue()); } if (checkResources && checkAbsoluteResources) { assertTrue("module.xml should contain two resource-root elements", nl.getLength() == 2); assertTrue("module.xml should contain resource-root element with atrribute path=\"Dummy.jar\"", paths.contains("Dummy.jar")); assertTrue("module.xml should contain resource-root element with atrribute path=\"" + jarFile2.getCanonicalPath() +"\"", paths.contains(jarFile2.getCanonicalPath())); } else if (checkResources && !checkAbsoluteResources) { assertTrue("module.xml should contain one resource-root elements", nl.getLength() == 1); assertTrue("module.xml should contain resource-root element with atrribute path=\"Dummy.jar\"", paths.contains("Dummy.jar")); } else if (!checkResources && checkAbsoluteResources) { assertTrue("module.xml should contain one resource-root elements", nl.getLength() == 1); assertTrue("module.xml should contain resource-root element with atrribute path=\"" + jarFile2.getCanonicalPath() +"\"", paths.contains(jarFile2.getCanonicalPath())); } // check properties XPathExpression propertiesExpr = xpath.compile("/module/properties/property"); nl = (NodeList) propertiesExpr.evaluate(doc, XPathConstants.NODESET); if(checkProperties) { assertTrue("module.xml should contain one property but it has " + nl.getLength() + " properties", nl.getLength() == 1); NamedNodeMap attributes = nl.item(0).getAttributes(); String name = attributes.getNamedItem("name").getNodeValue(); String value = attributes.getNamedItem("value").getNodeValue(); assertTrue("module.xml should contain property bat=man", name.equals("bat") && value.equals("man")); } else { assertTrue("module.xml shouldn't contain any properties but it has " + nl.getLength() + " properties", nl.getLength() == 0); } // check dependencies XPathExpression dependenciesNameAttrExpr = xpath.compile("/module/dependencies/module/@name"); nl = (NodeList) dependenciesNameAttrExpr.evaluate(doc, XPathConstants.NODESET); if (checkDependencies) { assertTrue("module.xml should contain two resource-root elements", nl.getLength() == 2); assertTrue("module.xml should contain module element with atrribute name=\"org.jboss.logging\"", nl.item(0).getNodeValue().equals("org.jboss.logging")); assertTrue("module.xml should contain module element with atrribute name=\"org.jboss.logmanager\"", nl.item(1).getNodeValue().equals("org.jboss.logmanager")); } else { assertTrue("module.xml shouldn't contain any dependencies but it has " + nl.getLength() + " dependencies", nl.getLength() == 0); } XPathExpression exportDependenciesNameAttrExpr = xpath.compile("/module/dependencies/module/@export"); nl = (NodeList) exportDependenciesNameAttrExpr.evaluate(doc, XPathConstants.NODESET); if (checkDependencies) { assertTrue("module.xml should contain one resource-root elements", nl.getLength() == 1); assertTrue("module.xml should contain module element with atrribute export=true", nl.item(0).getNodeValue().equals("true")); } else { assertTrue("module.xml shouldn't contain any dependencies but it has " + nl.getLength() + " dependencies", nl.getLength() == 0); } // check main class XPathExpression mainClassNameAttrExpr = xpath.compile("/module/main-class/@name"); nl = (NodeList) mainClassNameAttrExpr.evaluate(doc, XPathConstants.NODESET); if(checkMainClass) { assertTrue("module.xml should contain main-class element", nl.getLength() == 1); assertTrue("module.xml should contain main-class element with atrribute name=\"org.jboss.Test\"", nl.item(0).getNodeValue().equals("org.jboss.Test")); } else { assertTrue("module.xml shouldn't contain main-class element", nl.getLength() == 0); } } }