/* * JBoss, Home of Professional Open Source * Copyright 2010, Red Hat Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt 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.patching; import static org.jboss.as.patching.Constants.BASE; import static org.jboss.as.patching.IoUtils.mkdir; import static org.jboss.as.patching.IoUtils.newFile; import static org.jboss.as.test.patching.PatchingTestUtil.AS_DISTRIBUTION; import static org.jboss.as.test.patching.PatchingTestUtil.AS_VERSION; import static org.jboss.as.test.patching.PatchingTestUtil.FILE_SEPARATOR; import static org.jboss.as.test.patching.PatchingTestUtil.MODULES_PATH; import static org.jboss.as.test.patching.PatchingTestUtil.PATCHES_PATH; import static org.jboss.as.test.patching.PatchingTestUtil.PRODUCT; import static org.jboss.as.test.patching.PatchingTestUtil.createPatchXMLFile; import static org.jboss.as.test.patching.PatchingTestUtil.createZippedPatchFile; import static org.jboss.as.test.patching.PatchingTestUtil.dump; import static org.jboss.as.test.patching.PatchingTestUtil.randomString; import static org.jboss.as.test.patching.PatchingTestUtil.readFile; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import org.jboss.as.cli.CommandContext; import org.jboss.as.cli.CommandContextFactory; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.patching.HashUtils; import org.jboss.as.patching.metadata.ContentModification; import org.jboss.as.patching.metadata.Patch; import org.jboss.as.patching.metadata.PatchBuilder; import org.jboss.as.test.patching.util.module.Module; import org.jboss.as.test.shared.TestSuiteEnvironment; import org.jboss.as.version.ProductConfig; import org.jboss.dmr.ModelNode; import org.jboss.logging.Logger; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.wildfly.core.testrunner.ServerControl; import org.wildfly.core.testrunner.WildflyTestRunner; import com.google.common.base.Joiner; /** * @author Jan Martiska */ @RunWith(WildflyTestRunner.class) @ServerControl(manual = true) public class BasicOneOffPatchingScenariosTestCase extends AbstractPatchingTestCase { private static final Logger logger = Logger.getLogger(BasicOneOffPatchingScenariosTestCase.class); private static final String FILE1 = PatchingTestUtil.AS_DISTRIBUTION + FILE_SEPARATOR + "README.txt"; @BeforeClass public static void prepare() throws IOException { // README.txt does not exist on EAP final File file = new File(FILE1); if (!file.exists()) { dump(file, "test-content"); } } /** * Prepare a one-off patch which adds a misc file. Apply it, check that the file was created. * Roll it back, check that the file was deleted. */ @Test public void testOneOffPatchAddingAMiscFile() throws Exception { final String fileContent = "Hello World!"; // prepare the patch String patchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); ContentModification miscFileAdded = ContentModificationUtils.addMisc(oneOffPatchDir, patchID, fileContent, "awesomeDirectory", "awesomeFile"); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "main"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch adding a misc file.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileAdded) .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); controller.start(); String path = AS_DISTRIBUTION + FILE_SEPARATOR + Joiner.on(FILE_SEPARATOR).join(new String[]{"awesomeDirectory", "awesomeFile"}); Assert.assertTrue("File " + path + " should exist", new File(path).exists()); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertEquals("Unexpected contents of misc file", fileContent, readFile(path)); // rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); controller.start(); Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertFalse("File + " + path + " should have been deleted", new File(path).exists()); //reapply patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); controller.start(); Assert.assertTrue("File " + path + " should exist", new File(path).exists()); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertEquals("Unexpected contents of misc file", fileContent, readFile(path)); controller.stop(); } /** * Applies a patch using a client provided ModelControllerClient. * Test for WFCORE-526 * @param path the path to patch archive * @return returns true or throws an exception * @throws Exception if the patch command failed */ protected boolean applyPatch(String path) throws Exception { final CommandContext ctx = CommandContextFactory.getInstance().newCommandContext(); final ModelControllerClient client = ModelControllerClient.Factory.create(TestSuiteEnvironment.getServerAddress(), TestSuiteEnvironment.getServerPort()); ctx.bindClient(client); try { ctx.handle("patch apply " + path); } finally { // terminating the session closes the client too ctx.terminateSession(); } return true; } /** * Prepare 3 one-offs, apply them and do --rollback-to second patch */ @Test public void testOneOffPatchRollbackTo() throws Exception { final String fileContent = "Hello World!"; // prepare the patch String patchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); ContentModification miscFileAdded = ContentModificationUtils.addMisc(oneOffPatchDir, patchID, fileContent, "awesomeDirectory", "awesomeFile"); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "main"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch adding a misc file.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileAdded) .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // prepare the patch String patchID2 = randomString(); File oneOffPatchDir2 = mkdir(tempDir, patchID2); ContentModification miscFileAdded2 = ContentModificationUtils.addMisc(oneOffPatchDir2, patchID2, fileContent, "awesomeDirectory", "awesomeFile2"); Patch oneOffPatch2 = PatchBuilder.create() .setPatchId(patchID2) .setDescription("A one-off patch adding a misc file.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileAdded2) .build(); createPatchXMLFile(oneOffPatchDir2, oneOffPatch2); File zippedPatch2 = createZippedPatchFile(oneOffPatchDir2, patchID2); // prepare the patch String patchID3 = randomString(); File oneOffPatchDir3 = mkdir(tempDir, patchID3); ContentModification miscFileAdded3 = ContentModificationUtils.addMisc(oneOffPatchDir3, patchID3, fileContent, "awesomeDirectory", "awesomeFile3"); Patch oneOffPatch3 = PatchBuilder.create() .setPatchId(patchID3) .setDescription("A one-off patch adding a misc file.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileAdded3) .build(); createPatchXMLFile(oneOffPatchDir3, oneOffPatch3); File zippedPatch3 = createZippedPatchFile(oneOffPatchDir3, patchID3); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // apply second patch controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch2.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // apply third patch controller.start(); Assert.assertTrue("The patch " + patchID2 + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID2)); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch3.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // rollback to second patch controller.start(); Assert.assertTrue("The patch " + patchID3 + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID3)); Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackToPatch(patchID2)); controller.stop(); // verify that only first patch is installed controller.start(); Assert.assertFalse("The patch " + patchID3 + " should not be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID3)); Assert.assertFalse("The patch " + patchID2 + " should not be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID2)); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); controller.stop(); } /** * Prepare a one-off patch which adds multiple (2) misc files. Apply it, check that the files was created. * Roll it back, check that the files was deleted and apply it again to make sure re-applying works as expected */ @Test public void testOneOffPatchAddingMultipleMiscFiles() throws Exception { // prepare the patch String patchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); final String[] testFileSegments1 = new String[]{"testDir1", "testFile1.txt"}; final String testFilePath1 = AS_DISTRIBUTION + FILE_SEPARATOR + Joiner.on(FILE_SEPARATOR).join(testFileSegments1); final String testContent1 = "test content1"; final String[] testFileSegments2 = new String[]{"directory with spaces", "file with spaces"}; final String testFilePath2 = AS_DISTRIBUTION + FILE_SEPARATOR + Joiner.on(FILE_SEPARATOR).join(testFileSegments2); final String testContent2 = "test content2"; ContentModification miscFileAdded1 = ContentModificationUtils.addMisc(oneOffPatchDir, patchID, testContent1, testFileSegments1); ContentModification miscFileAdded2 = ContentModificationUtils.addMisc(oneOffPatchDir, patchID, testContent2, testFileSegments2); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "main"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch adding a misc file.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileAdded1) .addContentModification(miscFileAdded2) .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is installed, if files exists and check content of files controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("File1 " + testFilePath1 + " should exist", new File(testFilePath1).exists()); Assert.assertTrue("File2 " + testFilePath2 + " should exist", new File(testFilePath2).exists()); String patchContent = readFile(testFilePath1); Assert.assertEquals("check content of file1 after applying patch", testContent1, patchContent); patchContent = readFile(testFilePath2); Assert.assertEquals("check content of file2 after applying patch", testContent2, patchContent); // rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is uninstalled, if files don't exists controller.start(); Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertFalse("File1 + " + testFilePath1 + " should have been deleted", new File(testFilePath1).exists()); Assert.assertFalse("File2 + " + testFilePath1 + " should have been deleted", new File(testFilePath1).exists()); // reapply the patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is installed, if files exists and check content of files controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("File1 " + testFilePath1 + " should exist", new File(testFilePath1).exists()); Assert.assertTrue("File2 " + testFilePath2 + " should exist", new File(testFilePath2).exists()); patchContent = readFile(testFilePath1); Assert.assertEquals("check content of file1 after applying patch", testContent1, patchContent); patchContent = readFile(testFilePath2); Assert.assertEquals("check content of file2 after applying patch", testContent2, patchContent); controller.stop(); } /** * Prepare a one-off patch which modifies a misc file. Apply it, check that the file was replaced. * Roll it back, check that the file was restored successfully. */ @Test public void testOneOffPatchModifyingAMiscFile() throws Exception { // prepare the patch String patchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); final String testFilePath = AS_DISTRIBUTION + FILE_SEPARATOR + "README.txt"; final String testContent = "test content"; final String originalContent = readFile(testFilePath); ContentModification miscFileModified = ContentModificationUtils.modifyMisc(oneOffPatchDir, patchID, testContent, new File(testFilePath), "README.txt"); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "consoleSlot"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch modifying a misc file.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileModified) .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); //check if patch is installed, check content of file controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); String patchContent = readFile(testFilePath); Assert.assertEquals(testContent, patchContent); //rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); //check if patch is uninstalled, check content of file controller.start(); Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); patchContent = readFile(testFilePath); Assert.assertEquals("check content of file after rollback", originalContent, patchContent); //reapply the patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); //check if patch is installed, check content of file controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); patchContent = readFile(testFilePath); Assert.assertEquals("check content of file after reapplying", testContent, patchContent); controller.stop(); } /** * Prepare a one-off patch which modifies multiple (2) misc file. Apply it, check that the files was replaced. * Roll it back, check that the files was restored successfully and apply it again to make sure re-applying works as expected */ @Test public void testOneOffPatchModifyingMultipleMiscFiles() throws Exception { // prepare the patch String patchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); final String testFilePath1 = AS_DISTRIBUTION + FILE_SEPARATOR + "README.txt"; final String testContent1 = "test content1"; final String originalContent1 = readFile(testFilePath1); final String testFilePath2 = AS_DISTRIBUTION + FILE_SEPARATOR + "LICENSE.txt"; final String testContent2 = "test content2"; final String originalContent2 = readFile(testFilePath2); ContentModification miscFileModified1 = ContentModificationUtils.modifyMisc(oneOffPatchDir, patchID, testContent1, new File(testFilePath1), "README.txt"); ContentModification miscFileModified2 = ContentModificationUtils.modifyMisc(oneOffPatchDir, patchID, testContent2, new File(testFilePath2), "LICENSE.txt"); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "consoleSlot"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch modifying multiple misc files.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileModified1) .addContentModification(miscFileModified2) .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); //check content controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); String patchContent1 = readFile(testFilePath1); Assert.assertEquals("check content of applying patch1", testContent1, patchContent1); String patchContent2 = readFile(testFilePath2); Assert.assertEquals("check content of applying patch2", testContent2, patchContent2); //rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); //check content controller.start(); Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); patchContent1 = readFile(testFilePath1); Assert.assertEquals("check content of file after rollback1", originalContent1, patchContent1); patchContent2 = readFile(testFilePath2); Assert.assertEquals("check content of file after rollback2", originalContent2, patchContent2); //reapply the patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); //check content controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); patchContent1 = readFile(testFilePath1); Assert.assertEquals("check content of file after reapplying1", testContent1, patchContent1); patchContent2 = readFile(testFilePath2); Assert.assertEquals("check content of file after reapplying2", testContent2, patchContent2); controller.stop(); } /** * Prepare a one-off patch which removes a misc file. Apply it, check that the file was removed. * Roll it back, check that the file was restored and reapply patch */ @Test public void testOneOffPatchDeletingAMiscFile() throws Exception { // prepare the patch String patchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); final String testFilePath = AS_DISTRIBUTION + FILE_SEPARATOR + "README.txt"; // store original content String originalContent = readFile(testFilePath); ContentModification miscFileRemoved = ContentModificationUtils.removeMisc(new File(testFilePath), ""); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "main"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch removing a misc file.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileRemoved) .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check that patch is installed, file doesn't exist controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertFalse("File " + testFilePath + " should have been deleted", new File(testFilePath).exists()); // rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check that the patch is uninstalled and file is restored controller.start(); Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("File + " + testFilePath + " should be restored", new File(testFilePath).exists()); Assert.assertEquals("Unexpected contents of misc file", originalContent, readFile(testFilePath)); // reapply patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check that patch is installed, file doesn't exist controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertFalse("File " + testFilePath + " should have been deleted", new File(testFilePath).exists()); controller.stop(); } /** * Prepare a one-off patch which deletes multiple (2) misc files. Apply it, check that the file was removed. * Roll it back, check that the file was restored and reapply patch */ @Test public void testOneOffPatchDeletingMultipleMiscFiles() throws Exception { // prepare the patch String patchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); final String testFilePath1 = AS_DISTRIBUTION + FILE_SEPARATOR + "README.txt"; final String testFilePath2 = AS_DISTRIBUTION + FILE_SEPARATOR + "LICENSE.txt"; // store original content String originalContent1 = readFile(testFilePath1); String originalContent2 = readFile(testFilePath2); ContentModification miscFileRemoved1 = ContentModificationUtils.removeMisc(new File(testFilePath1), ""); ContentModification miscFileRemoved2 = ContentModificationUtils.removeMisc(new File(testFilePath2), ""); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "main"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch removing a misc file.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileRemoved1) .addContentModification(miscFileRemoved2) .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check that patch is installed, files don't exist controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertFalse("File " + testFilePath1 + " should have been deleted", new File(testFilePath1).exists()); Assert.assertFalse("File " + testFilePath2 + " should have been deleted", new File(testFilePath2).exists()); // rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check that the patch is uninstalled and file is restored controller.start(); Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("File + " + testFilePath1 + " should be restored", new File(testFilePath1).exists()); Assert.assertTrue("File + " + testFilePath2 + " should be restored", new File(testFilePath2).exists()); Assert.assertEquals("Unexpected contents of misc file", originalContent1, readFile(testFilePath1)); Assert.assertEquals("Unexpected contents of misc file", originalContent2, readFile(testFilePath2)); // reapply patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check that patch is installed, file doesn't exist controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertFalse("File " + testFilePath1 + " should have been deleted", new File(testFilePath1).exists()); Assert.assertFalse("File " + testFilePath2 + " should have been deleted", new File(testFilePath2).exists()); controller.stop(); } /** * Prepare a one-off patch which modifies misc file and deletes another misc file. Apply it, check that the files was replaced a deleted. * Roll it back, check that the files was restored, created and apply it again to make sure re-applying works as expected */ @Test public void testOneOffPatchModifyingAMiscFileDeletingAnotherMiscFile() throws Exception { // prepare the patch String patchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); final String testFilePath1 = AS_DISTRIBUTION + FILE_SEPARATOR + "README.txt"; final String testContent1 = "test content1"; final String originalContent1 = readFile(testFilePath1); final String testFilePathDeleted = AS_DISTRIBUTION + FILE_SEPARATOR + "LICENSE.txt"; final String originalContentOfDeletedFile = readFile(testFilePathDeleted); ContentModification miscFileModified = ContentModificationUtils.modifyMisc(oneOffPatchDir, patchID, testContent1, new File(testFilePath1), "README.txt"); ContentModification miscFileDeleted = ContentModificationUtils.removeMisc(new File(testFilePathDeleted), ""); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "main"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch modifying one misc file and deleting another misc file.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileModified) .addContentModification(miscFileDeleted) .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is installed, if files are created, deleted and check content controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertEquals("Unexpected contents of misc file", testContent1, readFile(testFilePath1)); Assert.assertFalse("File " + testFilePathDeleted + " should have been deleted", new File(testFilePathDeleted).exists()); // rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is uninstalled controller.start(); Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertEquals("Unexpected contents of misc file", originalContent1, readFile(testFilePath1)); Assert.assertTrue("File + " + testFilePathDeleted + " should exist", new File(testFilePathDeleted).exists()); Assert.assertEquals("Unexpected contents of misc file", originalContentOfDeletedFile, readFile(testFilePathDeleted)); // reapply the patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is installed, if files exists and check content of files controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertEquals("Unexpected contents of misc file", testContent1, readFile(testFilePath1)); Assert.assertFalse("File " + testFilePathDeleted + " should have been deleted", new File(testFilePathDeleted).exists()); controller.stop(); } /** * Prepare a one-off patch which modifies multiple (2) misc file and deletes multiple (2) misc files. Apply it, check that the files was replaced, deleted. * Roll it back, check that the files was restored successfully and apply it again to make sure re-applying works as expected */ @Test public void testOneOffPatchModifyingMultipleMiscFilesDeletingMultipleMiscFiles() throws Exception { // prepare the patch String patchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); final String testFilePath1 = AS_DISTRIBUTION + FILE_SEPARATOR + "README.txt"; final String testContent1 = "test content1"; final String originalContent1 = readFile(testFilePath1); final String testFilePath2 = AS_DISTRIBUTION + FILE_SEPARATOR + "LICENSE.txt"; final String testContent2 = "test content2"; final String originalContent2 = readFile(testFilePath2); final String[] testFilePathSegments1 = new String[]{"bin", "domain.sh"}; final String testFilePathDeleted1 = AS_DISTRIBUTION + FILE_SEPARATOR + Joiner.on(FILE_SEPARATOR).join(testFilePathSegments1); final String originalContentOfDeletedFile1 = readFile(testFilePathDeleted1); final String[] testFilePathSegments2 = new String[]{"bin", "standalone.sh"}; final String testFilePathDeleted2 = AS_DISTRIBUTION + FILE_SEPARATOR + Joiner.on(FILE_SEPARATOR).join(testFilePathSegments2); final String originalContentOfDeletedFile2 = readFile(testFilePathDeleted2); ContentModification miscFileModified1 = ContentModificationUtils.modifyMisc(oneOffPatchDir, patchID, testContent1, new File(testFilePath1), "README.txt"); ContentModification miscFileModified2 = ContentModificationUtils.modifyMisc(oneOffPatchDir, patchID, testContent2, new File(testFilePath2), "LICENSE.txt"); ContentModification miscFileDeleted1 = ContentModificationUtils.removeMisc(new File(testFilePathDeleted1), testFilePathSegments1); ContentModification miscFileDeleted2 = ContentModificationUtils.removeMisc(new File(testFilePathDeleted2), testFilePathSegments2); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "consoleSlot"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch modifying multiple misc files.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileModified1) .addContentModification(miscFileModified2) .addContentModification(miscFileDeleted1) .addContentModification(miscFileDeleted2) .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); //check content controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertEquals("Unexpected contents of misc file", testContent1, readFile(testFilePath1)); Assert.assertEquals("Unexpected contents of misc file", testContent2, readFile(testFilePath2)); Assert.assertFalse("File " + testFilePathDeleted1 + " should have been deleted", new File(testFilePathDeleted1).exists()); Assert.assertFalse("File " + testFilePathDeleted2 + " should have been deleted", new File(testFilePathDeleted2).exists()); //rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); //check content controller.start(); Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertEquals("Unexpected contents of misc file", originalContent1, readFile(testFilePath1)); Assert.assertEquals("Unexpected contents of misc file", originalContent2, readFile(testFilePath2)); Assert.assertTrue("File + " + testFilePathDeleted1 + " should exist", new File(testFilePathDeleted1).exists()); Assert.assertEquals("Unexpected contents of misc file", originalContentOfDeletedFile1, readFile(testFilePathDeleted1)); Assert.assertTrue("File + " + testFilePathDeleted2 + " should exist", new File(testFilePathDeleted2).exists()); Assert.assertEquals("Unexpected contents of misc file", originalContentOfDeletedFile2, readFile(testFilePathDeleted2)); //reapply the patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); //check content controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertEquals("Unexpected contents of misc file", testContent1, readFile(testFilePath1)); Assert.assertEquals("Unexpected contents of misc file", testContent2, readFile(testFilePath2)); Assert.assertFalse("File " + testFilePathDeleted1 + " should have been deleted", new File(testFilePathDeleted1).exists()); Assert.assertFalse("File " + testFilePathDeleted2 + " should have been deleted", new File(testFilePathDeleted2).exists()); controller.stop(); } /** * Prepare a one-off patch which adds misc file and deletes another misc file. Apply it, check that the files was created a deleted. * Roll it back, check that the files was deleted, created and apply it again to make sure re-applying works as expected */ @Test public void testOneOffPatchAddingAMiscFileDeletingAnotherMiscFile() throws Exception { // prepare the patch String patchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); final String[] testFileSegmentsAdded = new String[]{"testDir1", "testFile1.txt"}; final String testFilePathAdded = AS_DISTRIBUTION + FILE_SEPARATOR + Joiner.on(FILE_SEPARATOR).join(testFileSegmentsAdded); final String testContentAdded = "test content1"; final String testFilePathDeleted = AS_DISTRIBUTION + FILE_SEPARATOR + "LICENSE.txt"; final String originalContentOfDeletedFile = readFile(testFilePathDeleted); ContentModification miscFileAdded = ContentModificationUtils.addMisc(oneOffPatchDir, patchID, testContentAdded, testFileSegmentsAdded); ContentModification miscFileDeleted = ContentModificationUtils.removeMisc( new File(testFilePathDeleted), ""); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "main"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch adding one misc file and deleting another misc file.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileAdded) .addContentModification(miscFileDeleted) .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is installed, if files are created, deleted and check content controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("File1 " + testFilePathAdded + " should exist", new File(testFilePathAdded).exists()); Assert.assertFalse("File2 " + testFilePathDeleted + " should have been deleted", new File(testFilePathDeleted).exists()); Assert.assertEquals("check content of file after applying patch", testContentAdded, readFile(testFilePathAdded)); // rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is uninstalled controller.start(); Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertFalse("File1 + " + testFilePathAdded + " should have been deleted", new File(testFilePathAdded).exists()); Assert.assertTrue("File2 + " + testFilePathDeleted + " should exist", new File(testFilePathDeleted).exists()); Assert.assertEquals("check content of file after patch rollback", originalContentOfDeletedFile, readFile(testFilePathDeleted)); // reapply the patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is installed, if files exists and check content of files controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("File1 " + testFilePathAdded + " should exist", new File(testFilePathAdded).exists()); Assert.assertFalse("File2 " + testFilePathDeleted + " should have been deleted", new File(testFilePathDeleted).exists()); Assert.assertEquals("check content of file after applying patch", testContentAdded, readFile(testFilePathAdded)); controller.stop(); } /** * Prepare a one-off patch which adds multiple misc files and deletes multiple misc files. Apply it, check that the files was created a deleted. * Roll it back, check that the files was deleted, created and apply it again to make sure re-applying works as expected */ @Test public void testOneOffPatchAddingMultipleMiscFilesDeletingMultipleMiscFiles() throws Exception { // prepare the patch String patchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); final String[] testFileSegmentsAdded1 = new String[]{"testDir1", "testFile2.txt"}; final String testFilePathAdded1 = AS_DISTRIBUTION + FILE_SEPARATOR + Joiner.on(FILE_SEPARATOR).join(testFileSegmentsAdded1); final String testContentAdded1 = "test content2"; final String[] testFileSegmentsAdded2 = new String[]{"testFile2.txt"}; final String testFilePathAdded2 = AS_DISTRIBUTION + FILE_SEPARATOR + Joiner.on(FILE_SEPARATOR).join(testFileSegmentsAdded2); final String testContentAdded2 = "test content2"; final String testFilePathDeleted1 = AS_DISTRIBUTION + FILE_SEPARATOR + "README.txt"; final String testFilePathDeleted2 = AS_DISTRIBUTION + FILE_SEPARATOR + "LICENSE.txt"; final String originalContentOfDeletedFile1 = readFile(testFilePathDeleted1); final String originalContentOfDeletedFile2 = readFile(testFilePathDeleted2); ContentModification miscFileAdded1 = ContentModificationUtils.addMisc(oneOffPatchDir, patchID, testContentAdded1, testFileSegmentsAdded1); ContentModification miscFileAdded2 = ContentModificationUtils.addMisc(oneOffPatchDir, patchID, testContentAdded2, testFileSegmentsAdded2); ContentModification miscFileDeleted1 = ContentModificationUtils.removeMisc(new File(testFilePathDeleted1), ""); ContentModification miscFileDeleted2 = ContentModificationUtils.removeMisc(new File(testFilePathDeleted2), ""); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "main"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch adding one misc file and deleting another misc file.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .addContentModification(miscFileAdded1) .addContentModification(miscFileAdded2) .addContentModification(miscFileDeleted1) .addContentModification(miscFileDeleted2) .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is installed, if files are created, deleted and check content controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("File1 " + testFilePathAdded1 + " should exist", new File(testFilePathAdded1).exists()); Assert.assertTrue("File2 " + testFilePathAdded2 + " should exist", new File(testFilePathAdded2).exists()); Assert.assertFalse("File3 " + testFilePathDeleted1 + " should have been deleted", new File(testFilePathDeleted1).exists()); Assert.assertFalse("File4 " + testFilePathDeleted2 + " should have been deleted", new File(testFilePathDeleted2).exists()); Assert.assertEquals("check content of file after applying patch", testContentAdded1, readFile(testFilePathAdded1)); Assert.assertEquals("check content of file after applying patch", testContentAdded2, readFile(testFilePathAdded2)); // rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is uninstalled controller.start(); Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertFalse("File1 + " + testFilePathAdded1 + " should have been deleted", new File(testFilePathAdded1).exists()); Assert.assertFalse("File2 + " + testFilePathAdded2 + " should have been deleted", new File(testFilePathAdded2).exists()); Assert.assertTrue("File3 + " + testFilePathDeleted1 + " should exist", new File(testFilePathDeleted1).exists()); Assert.assertTrue("File4 + " + testFilePathDeleted2 + " should exist", new File(testFilePathDeleted2).exists()); Assert.assertEquals("check content of file after patch rollback", originalContentOfDeletedFile1, readFile(testFilePathDeleted1)); Assert.assertEquals("check content of file after patch rollback", originalContentOfDeletedFile2, readFile(testFilePathDeleted2)); // reapply the patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is installed, if files exists and check content of files controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("File1 " + testFilePathAdded1 + " should exist", new File(testFilePathAdded1).exists()); Assert.assertTrue("File2 " + testFilePathAdded2 + " should exist", new File(testFilePathAdded2).exists()); Assert.assertFalse("File3 " + testFilePathDeleted1 + " should have been deleted", new File(testFilePathDeleted1).exists()); Assert.assertFalse("File4 " + testFilePathDeleted2 + " should have been deleted", new File(testFilePathDeleted2).exists()); Assert.assertEquals("check content of file after applying patch", testContentAdded1, readFile(testFilePathAdded1)); Assert.assertEquals("check content of file after applying patch", testContentAdded2, readFile(testFilePathAdded2)); controller.stop(); } /** * Prepare a one-off patch which adds a new module "org.wildfly.awesomemodule" to the base layer. Apply it, check that the module was installed * Roll it back, check that the files was deleted, created and apply it again to make sure re-applying works as expected * * @throws Exception */ @Test public void testOneOffPatchAddingAModule() throws Exception { // prepare the patch String patchID = randomString(); String invalidPatchId = randomString(); String layerPatchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); File invalidOneOffPatchDir = mkdir(tempDir, invalidPatchId); final String moduleName = "org.wildfly.test." + randomString(); final String modulePath = PATCHES_PATH + FILE_SEPARATOR + layerPatchID + FILE_SEPARATOR + moduleName.replace(".", FILE_SEPARATOR) + FILE_SEPARATOR + "main"; final ResourceItem resourceItem1 = new ResourceItem("testFile1", "content1".getBytes(StandardCharsets.UTF_8)); final ResourceItem resourceItem2 = new ResourceItem("testFile2", "content2".getBytes(StandardCharsets.UTF_8)); Module newModule = new Module.Builder(moduleName) .miscFile(resourceItem1) .miscFile(resourceItem2) .build(); // create valid patch ContentModification moduleAdded = ContentModificationUtils.addModule(oneOffPatchDir, layerPatchID, newModule); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "main"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch adding a new module.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .oneOffPatchElement(layerPatchID, "base", false) .setDescription("New module for the base layer") .addContentModification(moduleAdded) .getParent() .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // create invalid patch - replaced layerPatchId with patchID ContentModification invalidContentModification = ContentModificationUtils.addModule(invalidOneOffPatchDir, invalidPatchId, newModule); Patch invalidOneOffPatch = PatchBuilder.create() .setPatchId(invalidPatchId) .setDescription("A invalid one-off patch adding a new module.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .oneOffPatchElement(layerPatchID, "base", false) .setDescription("New module for the base layer") .addContentModification(invalidContentModification) .getParent() .build(); createPatchXMLFile(invalidOneOffPatchDir, invalidOneOffPatch); File zippedInvalidPatch = createZippedPatchFile(invalidOneOffPatchDir, invalidPatchId); // try to apply invalid one off patch and apply valid patch and check if server is in restart-required mode controller.start(); Assert.assertFalse("Patch shouldn't be accepted", CliUtilsForPatching.applyPatch(zippedInvalidPatch.getAbsolutePath())); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is listed as installed, files exists on correct place controller.start(); List<String> paths = CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName, false); Assert.assertTrue("Module should be loaded from the .overlays directory but was: " + paths.get(0), paths.get(0).contains(".overlays" + File.separator + layerPatchID)); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("The file " + resourceItem1.getItemName() + " should exist", new File(modulePath + FILE_SEPARATOR + resourceItem1.getItemName()).exists()); Assert.assertTrue("The file " + resourceItem2.getItemName() + " should exist", new File(modulePath + FILE_SEPARATOR + resourceItem2.getItemName()).exists()); // look into patch history List<ModelNode> history = CliUtilsForPatching.getPatchingHistory(); Assert.assertTrue("Patch " + patchID + " should be visible in history: " + Arrays.deepToString(history.toArray()), PatchingTestUtil.isOneOffPatchContainedInHistory(history, patchID)); // rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is not listed controller.start(); // check that module is not active try { CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName, true); Assert.fail("Module " + moduleName + " should have been removed"); } catch (RuntimeException expected) { } Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertFalse("The file " + resourceItem1.getItemName() + "should have been deleted", new File(modulePath + FILE_SEPARATOR + resourceItem1.getItemName()).exists()); Assert.assertFalse("The file " + resourceItem2.getItemName() + "should have been deleted", new File(modulePath + FILE_SEPARATOR + resourceItem2.getItemName()).exists()); // reapply patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is listed as installed, files exists on correct place controller.start(); paths = CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName, false); Assert.assertTrue("Module should be loaded from the .overlays directory but was: " + paths.get(0), paths.get(0).contains(".overlays" + File.separator + layerPatchID)); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("The file " + resourceItem1.getItemName() + " should exist", new File(modulePath + FILE_SEPARATOR + resourceItem1.getItemName()).exists()); Assert.assertTrue("The file " + resourceItem2.getItemName() + " should exist", new File(modulePath + FILE_SEPARATOR + resourceItem2.getItemName()).exists()); controller.stop(); } /** * Prepare a one-off patch which adds multiple (2) modules "org.wildfly.awesomemodule(#)" to the base layer. Apply it, check that the modules were installed * Roll it back, check that the modules were uninstalled and apply it again to make sure re-applying works as expected * * @throws Exception */ @Test public void testOneOffPatchAddingMultipleModules() throws Exception { // prepare the patch String patchID = randomString(); String layerPatchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); final String moduleName1 = "org.wildfly.test." + randomString(); final String modulePath1 = PATCHES_PATH + FILE_SEPARATOR + layerPatchID + FILE_SEPARATOR + moduleName1.replace(".", FILE_SEPARATOR) + FILE_SEPARATOR + "main"; final String moduleName2 = "org.wildfly.test." + randomString(); final String modulePath2 = PATCHES_PATH + FILE_SEPARATOR + layerPatchID + FILE_SEPARATOR + moduleName2.replace(".", FILE_SEPARATOR) + FILE_SEPARATOR + "main"; final ResourceItem resourceItem1 = new ResourceItem("testFile1", "content1".getBytes(StandardCharsets.UTF_8)); final ResourceItem resourceItem2 = new ResourceItem("testFile2", "content2".getBytes(StandardCharsets.UTF_8)); Module newModule1 = new Module.Builder(moduleName1) .miscFile(resourceItem1) .miscFile(resourceItem2) .build(); Module newModule2 = new Module.Builder(moduleName2) .miscFile(resourceItem1) .miscFile(resourceItem2) .build(); ContentModification moduleAdded1 = ContentModificationUtils.addModule(oneOffPatchDir, layerPatchID, newModule1); ContentModification moduleAdded2 = ContentModificationUtils.addModule(oneOffPatchDir, layerPatchID, newModule2); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "main"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch adding multiple modules.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .oneOffPatchElement(layerPatchID, "base", false) .setDescription("New modules for the base layer") .addContentModification(moduleAdded1) .addContentModification(moduleAdded2) .getParent() .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is listed as installed, files exists on correct place controller.start(); List<String> paths = CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName1, false); Assert.assertTrue("Module should be loaded from the .overlays directory but was: " + paths.get(0), paths.get(0).contains(".overlays" + File.separator + layerPatchID)); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("The file " + resourceItem1.getItemName() + " should exist", new File(modulePath1 + FILE_SEPARATOR + resourceItem1.getItemName()).exists()); Assert.assertTrue("The file " + resourceItem2.getItemName() + " should exist", new File(modulePath1 + FILE_SEPARATOR + resourceItem2.getItemName()).exists()); Assert.assertTrue("The file " + resourceItem1.getItemName() + " should exist", new File(modulePath2 + FILE_SEPARATOR + resourceItem1.getItemName()).exists()); Assert.assertTrue("The file " + resourceItem2.getItemName() + " should exist", new File(modulePath2 + FILE_SEPARATOR + resourceItem2.getItemName()).exists()); // rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is not listed controller.start(); // check that module1 is not active try { CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName1, true); Assert.fail("Module " + moduleName1 + " should have been removed by the patch"); } catch (RuntimeException expected) { } Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertFalse("The file " + resourceItem1.getItemName() + "should have been deleted", new File(modulePath1 + FILE_SEPARATOR + resourceItem1.getItemName()).exists()); Assert.assertFalse("The file " + resourceItem2.getItemName() + "should have been deleted", new File(modulePath1 + FILE_SEPARATOR + resourceItem2.getItemName()).exists()); Assert.assertFalse("The file " + resourceItem1.getItemName() + "should have been deleted", new File(modulePath2 + FILE_SEPARATOR + resourceItem1.getItemName()).exists()); Assert.assertFalse("The file " + resourceItem2.getItemName() + "should have been deleted", new File(modulePath2 + FILE_SEPARATOR + resourceItem2.getItemName()).exists()); Assert.assertFalse("The directory " + modulePath1 + "should have been deleted", new File(modulePath1).exists()); Assert.assertFalse("The directory " + modulePath2 + "should have been deleted", new File(modulePath2).exists()); // reapply patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is listed as installed, files exists on correct place controller.start(); paths = CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName1, false); Assert.assertTrue("Module should be loaded from the .overlays directory but was: " + paths.get(0), paths.get(0).contains(".overlays" + File.separator + layerPatchID)); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("The file " + resourceItem1.getItemName() + " should exist", new File(modulePath1 + FILE_SEPARATOR + resourceItem1.getItemName()).exists()); Assert.assertTrue("The file " + resourceItem2.getItemName() + " should exist", new File(modulePath1 + FILE_SEPARATOR + resourceItem2.getItemName()).exists()); Assert.assertTrue("The file " + resourceItem1.getItemName() + " should exist", new File(modulePath2 + FILE_SEPARATOR + resourceItem1.getItemName()).exists()); Assert.assertTrue("The file " + resourceItem2.getItemName() + " should exist", new File(modulePath2 + FILE_SEPARATOR + resourceItem2.getItemName()).exists()); controller.stop(); } /** * Create a new module in AS distribution, * create a patch which modifies it by adding a new text file into it. */ @Test public void testModifyAModule() throws Exception { ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "main"); final String moduleName = "org.wildfly.test." + randomString(); // add a new empty module to eap dist Module module = new Module.Builder(moduleName).build(); File moduleDir = module.writeToDisk(new File(MODULES_PATH)); logger.tracef("moduleDir = %s", moduleDir.getAbsolutePath()); // prepare the patch String patchID = randomString(); String baseLayerPatchID = randomString(); File patchDir = mkdir(tempDir, patchID); Module updatedModule = new Module.Builder(moduleName) .miscFile(new ResourceItem("res1", "new resource in the module".getBytes(StandardCharsets.UTF_8))) .build(); // Also see if we can update jboss-modules final File installation = new File(AS_DISTRIBUTION); final ContentModification jbossModulesModification = PatchingTestUtil.updateModulesJar(installation, new File(patchDir, baseLayerPatchID)); // create the patch with the updated module ContentModification moduleModified = ContentModificationUtils.modifyModule(patchDir, baseLayerPatchID, HashUtils.hashFile(moduleDir), updatedModule); Patch patch = PatchBuilder.create() .setPatchId(patchID) .setDescription(randomString()) .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .oneOffPatchElement(baseLayerPatchID, BASE, false) .addContentModification(moduleModified) .addContentModification(jbossModulesModification) .getParent() .build(); createPatchXMLFile(patchDir, patch); File zippedPatch = createZippedPatchFile(patchDir, patchID); logger.info(zippedPatch.getAbsolutePath()); // apply patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); File newFile = newFile(new File(PATCHES_PATH), baseLayerPatchID); newFile = newFile(newFile, moduleName.split("\\.")); newFile = newFile(newFile, "main", "res1"); Assert.assertTrue("File " + newFile.getAbsolutePath() + " should exist", newFile.exists()); // check that JBoss Modules picks up the module from the right path List<String> paths = CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName, false); Assert.assertTrue("Module should be loaded from the .overlays directory but was: " + paths.get(0), paths.get(0).contains(".overlays" + File.separator + baseLayerPatchID)); // rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); controller.start(); Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertFalse("File " + newFile.getAbsolutePath() + " should not exist", newFile.exists()); paths = CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName, false); Assert.assertFalse("Module should NOT be loaded from the .overlays directory but was: " + paths.get(0), paths.get(0).contains(".overlays" + File.separator + baseLayerPatchID)); // reapply patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); // check that JBoss Modules picks up the module from the right path paths = CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName, false); Assert.assertTrue("Module should be loaded from the .overlays directory but was: " + paths.get(0), paths.get(0).contains(".overlays" + File.separator + baseLayerPatchID)); Assert.assertTrue("File " + newFile.getAbsolutePath() + " should exist", newFile.exists()); controller.stop(); } /** * Prepare a one-off patch which removes module. Apply it, check that the module was removed * Roll it back, check that the module was restored apply it again to make sure re-applying works as expected * * @throws Exception */ @Test public void testOneOffPatchRemovingAModule() throws Exception { // prepare the patch String patchID = randomString(); String layerPatchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); // creates an empty module final String moduleName = "org.wildfly.test." + randomString(); Module module = new Module.Builder(moduleName).build(); File moduleDir = module.writeToDisk(new File(MODULES_PATH)); File patchModuleDir = newFile(new File(PATCHES_PATH), layerPatchID); patchModuleDir = newFile(patchModuleDir, moduleName.split("\\.")); File moduleXml = newFile(patchModuleDir, "main", "module.xml"); ContentModification moduleRemoved = ContentModificationUtils.removeModule(moduleName, moduleDir); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "main"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch removing a module.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .oneOffPatchElement(layerPatchID, "base", false) .setDescription("Remove module") .addContentModification(moduleRemoved) .getParent() .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is listed as installed, files exists on correct place controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("The file " + moduleXml.getName() + " should exist", moduleXml.exists()); // check that the module is not active try { List<String> paths = CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName, true); Assert.fail("Module " + moduleName + " should have been removed by the patch"); } catch (RuntimeException expected) { } // rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is not listed controller.start(); // check that the module exists CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName, true); Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName, true); // reapply patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is listed as installed, files exists on correct place controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); Assert.assertTrue("The file " + moduleXml.getName() + " should exist", moduleXml.exists()); // check that the module is not active try { CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName, true); Assert.fail("Module " + moduleName + " should have been removed by the patch"); } catch (RuntimeException expected) { } controller.stop(); } /** * Prepare a one-off patch which removes multiple (2) modules. Apply it, check that the modules were removed * Roll it back, check that the modules were restored apply it again to make sure re-applying works as expected * * @throws Exception */ @Test public void testOneOffPatchRemovingMultipleModules() throws Exception { // prepare the patch String patchID = randomString(); String layerPatchID = randomString(); File oneOffPatchDir = mkdir(tempDir, patchID); // creates an empty module final String moduleName1 = "org.wildfly.test." + randomString(); Module module1 = new Module.Builder(moduleName1).build(); File moduleDir1 = module1.writeToDisk(new File(MODULES_PATH)); File patchModuleDir1 = newFile(new File(PATCHES_PATH), layerPatchID); patchModuleDir1 = newFile(patchModuleDir1, moduleName1.split("\\.")); File moduleXml1 = newFile(patchModuleDir1, "main", "module.xml"); final String moduleName2 = "org.wildfly.test." + randomString(); Module module2 = new Module.Builder(moduleName2).build(); File moduleDir2 = module2.writeToDisk(new File(MODULES_PATH)); File patchModuleDir2 = newFile(new File(PATCHES_PATH), layerPatchID); patchModuleDir2 = newFile(patchModuleDir2, moduleName2.split("\\.")); File moduleXml2 = newFile(patchModuleDir2, "main", "module.xml"); ContentModification moduleRemoved1 = ContentModificationUtils.removeModule(moduleName1, moduleDir1); ContentModification moduleRemoved2 = ContentModificationUtils.removeModule(moduleName2, moduleDir2); ProductConfig productConfig = new ProductConfig(PRODUCT, AS_VERSION, "main"); Patch oneOffPatch = PatchBuilder.create() .setPatchId(patchID) .setDescription("A one-off patch removing multiple modules.") .oneOffPatchIdentity(productConfig.getProductName(), productConfig.getProductVersion()) .getParent() .oneOffPatchElement(layerPatchID, "base", false) .setDescription("Remove modules") .addContentModification(moduleRemoved1) .addContentModification(moduleRemoved2) .getParent() .build(); createPatchXMLFile(oneOffPatchDir, oneOffPatch); File zippedPatch = createZippedPatchFile(oneOffPatchDir, patchID); // apply the patch and check if server is in restart-required mode controller.start(); Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is listed as installed, files exists on correct place controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); // check that the module is not active try { CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName1, true); Assert.fail("Module " + moduleName1 + " should have been removed by the patch"); } catch (RuntimeException expected) { } // rollback the patch and check if server is in restart-required mode Assert.assertTrue("Rollback should be accepted", CliUtilsForPatching.rollbackPatch(patchID)); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is not listed controller.start(); Assert.assertFalse("The patch " + patchID + " NOT should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); // check that module1 is active CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName1, true); // reapply patch and check if server is in restart-required mode Assert.assertTrue("Patch should be accepted", CliUtilsForPatching.applyPatch(zippedPatch.getAbsolutePath())); Assert.assertTrue("server should be in restart-required mode", CliUtilsForPatching.doesServerRequireRestart()); controller.stop(); // check if patch is listed as installed, files exists on correct place controller.start(); Assert.assertTrue("The patch " + patchID + " should be listed as installed", CliUtilsForPatching.getInstalledPatches().contains(patchID)); // check that module1 is not active try { CliUtilsForPatching.getResourceLoaderPathsForModule(moduleName1, true); Assert.fail("Module " + moduleName1 + " should have been removed by the patch"); } catch (RuntimeException expected) { } controller.stop(); } }