package org.jboss.windup.addon.ui;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.io.FileUtils;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.forge.addon.dependencies.Coordinate;
import org.jboss.forge.addon.dependencies.DependencyResolver;
import org.jboss.forge.addon.dependencies.builder.CoordinateBuilder;
import org.jboss.forge.addon.dependencies.builder.DependencyQueryBuilder;
import org.jboss.forge.addon.ui.controller.CommandController;
import org.jboss.forge.addon.ui.result.Failed;
import org.jboss.forge.addon.ui.result.Result;
import org.jboss.forge.addon.ui.test.UITestHarness;
import org.jboss.forge.arquillian.AddonDependencies;
import org.jboss.forge.arquillian.AddonDependency;
import org.jboss.forge.arquillian.archive.AddonArchive;
import org.jboss.forge.furnace.Furnace;
import org.jboss.forge.furnace.addons.Addon;
import org.jboss.forge.furnace.addons.AddonId;
import org.jboss.forge.furnace.manager.AddonManager;
import org.jboss.forge.furnace.manager.request.InstallRequest;
import org.jboss.forge.furnace.manager.request.RemoveRequest;
import org.jboss.forge.furnace.util.Addons;
import org.jboss.forge.furnace.util.OperatingSystemUtils;
import org.jboss.forge.furnace.versions.SingleVersion;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.windup.exec.configuration.options.InputPathOption;
import org.jboss.windup.exec.configuration.options.OutputPathOption;
import org.jboss.windup.exec.updater.RulesetsUpdater;
import org.jboss.windup.ui.DistributionUpdater;
import org.jboss.windup.ui.WindupCommand;
import org.jboss.windup.ui.WindupUpdateDistributionCommand;
import org.jboss.windup.util.Logging;
import org.jboss.windup.util.PathUtil;
import org.jboss.windup.util.ZipUtil;
import org.jboss.windup.util.exception.WindupException;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* @author Ondrej Zizka, ozizka at redhat.com
*/
@RunWith(Arquillian.class)
public class WindupUpdateDistributionCommandTest
{
private static final Logger log = Logging.get(WindupUpdateDistributionCommandTest.class);
private static final String WINDUP_UI_ADDON_NAME = "org.jboss.windup.ui:windup-ui";
private static final String WINDUP_OLD_VERSION = "2.2.0.Final";
@Deployment
@AddonDependencies({
@AddonDependency(name = "org.jboss.forge.furnace.container:cdi"),
@AddonDependency(name = "org.jboss.windup.utils:windup-utils"),
@AddonDependency(name = WINDUP_UI_ADDON_NAME),
@AddonDependency(name = "org.jboss.windup.exec:windup-exec"),
@AddonDependency(name = "org.jboss.windup.graph:windup-graph"),
@AddonDependency(name = "org.jboss.windup.rules.apps:windup-rules-java"),
@AddonDependency(name = "org.jboss.forge.addon:maven"),
@AddonDependency(name = "org.jboss.forge.addon:addon-manager"),
@AddonDependency(name = "org.jboss.forge.addon:ui-test-harness"),
})
public static AddonArchive getDeployment()
{
AddonArchive archive = ShrinkWrap
.create(AddonArchive.class)
.addBeansXML()
.addAsResource(WindupCommandTest.class.getResource(TEST_RULESET_ZIP), TEST_RULESET_ZIP);
return archive;
}
private static String TEST_RULESET_ZIP = "/windup-old-ruleset.zip";
@Inject
private DependencyResolver resolver;
@Inject
private Addon addon;
@Inject
private Furnace furnace;
@Inject
private UITestHarness uiTestHarness;
@Inject
private RulesetsUpdater updater;
@Inject
private DistributionUpdater distUpdater;
@Inject
private AddonManager manager;
@Test
@Ignore("The current implementation doesn't work as it tampers with addons loaded at the time."
+ " Even if it replaced all Windup addons, it would still fail on Windows as they keep the .jar's locked.")
public void testUpdateDistribution() throws Exception
{
// Download and unzip an old distribution.
final CoordinateBuilder coords = CoordinateBuilder.create()
.setGroupId("org.jboss.windup")
.setArtifactId("windup-distribution")
.setClassifier("offline")
.setVersion("2.2.0.Final")
.setPackaging("zip");
System.out.println("Downloading " + coords + ", may take a while.");
List<Coordinate> results = resolver.resolveVersions(DependencyQueryBuilder.create(coords));
File windupDir = OperatingSystemUtils.createTempDir();
this.updater.extractArtifact(results.get(0), windupDir);
windupDir = DistributionUpdater.getWindupDistributionSubdir(windupDir);
Assert.assertTrue(windupDir.exists());
System.setProperty(PathUtil.WINDUP_HOME, windupDir.getAbsolutePath());
// Run the upgrader.
distUpdater.replaceWindupDirectoryWithLatestDistribution();
// Check the new version.
String newUiVersion = getInstalledAddonVersion(windupDir.toPath().resolve("addons").toString(), WINDUP_UI_ADDON_NAME);
Assert.assertTrue(new SingleVersion(newUiVersion).compareTo(new SingleVersion("2.2.0.Final")) > 0);
// Try to run Windup from there.
// TODO: I need to set the harness addons directory to the freshly created dir.
UITestHarness harness = furnace.getAddonRegistry().getServices(UITestHarness.class).get();
try (CommandController controller = harness.createCommandController(WindupCommand.class))
{
controller.initialize();
controller.setValueFor(InputPathOption.NAME, Collections.singletonList(new File("src/test/resources/test.jar").getAbsolutePath()));
final File resultDir = new File("target/testRunFromUpgraded");
resultDir.mkdirs();
controller.setValueFor(OutputPathOption.NAME, resultDir.getAbsolutePath());
Result result = controller.execute();
Assert.assertTrue(result.getMessage(), !(result instanceof Failed));
}
catch (Throwable ex)
{
throw new WindupException("Failed running Windup from the upgraded directory: " + ex.getMessage(), ex);
}
}
@Test
@Ignore("Completely broken now. New Furnace doesn't deal well with setting windup.home like this.")
public void testUpdateDistributionCommand() throws Exception
{
// Unzip the rulesets from a .zip in resources.
File tempDir = OperatingSystemUtils.createTempDir();
tempDir.deleteOnExit();
File extractedPath = new File(tempDir, "extracted-rulesets");
ZipUtil.unzipFromClassResource(getClass(), WindupUpdateDistributionCommandTest.TEST_RULESET_ZIP, extractedPath);
String windupDir = extractedPath + "/windup-old-ruleset";
System.setProperty(PathUtil.WINDUP_RULESETS_DIR_SYSPROP, windupDir);
File addonsDir = new File(windupDir, "addons");
addonsDir.mkdirs();
String currentUiVersion = getInstalledAddonVersion(addon.getRepository().getRootDirectory().getPath(), WINDUP_UI_ADDON_NAME);
installOldAddonVersion(currentUiVersion); // changeUiAddonVersion(addon.getRepository().getRootDirectory(),
// currentUiVersion);
waitForOldWindupUIAddon(furnace);
boolean rulesetNeedUpdate = updater.rulesetsNeedUpdate(true);
Assert.assertTrue(rulesetNeedUpdate);
try (CommandController controller = uiTestHarness.createCommandController("Windup Update Distribution"))
{
try
{
controller.initialize();
Assert.assertTrue(controller.isEnabled());
// Actually runs the command.
Result result = controller.execute();
Assert.assertFalse("Windup Update Distribution command should suceed, but it failed.", result instanceof Failed);
rulesetNeedUpdate = updater.rulesetsNeedUpdate(true);
Assert.assertFalse("Ruleset should have already been updated to the latest version and as such should not need another update.",
rulesetNeedUpdate);
checkWindupDirectory(windupDir);
}
finally
{
FileUtils.deleteDirectory(tempDir);
}
}
}
private void checkWindupDirectory(String windupDir)
{
File addonsHomeNew = new File(windupDir, "addons");
Assert.assertTrue("Addons folder was not updated sucessfully", addonsHomeNew.exists());
Assert.assertTrue("Addons folder does not contain enough addons (at least 6)", addonsHomeNew.listFiles().length > 5);
File binNew = new File(windupDir, "bin");
Assert.assertTrue("Bin folder was not updated sucessfully", binNew.exists());
Assert.assertTrue("Binary folder does not contain enough items (at least 2)", binNew.listFiles().length > 1);
File libNew = new File(windupDir, "lib");
Assert.assertTrue("Library folder was not updated sucessfully", libNew.exists());
Assert.assertTrue("Library folder does not contain enough libraries (at least 8)", libNew.listFiles().length > 7);
}
private void waitForOldWindupUIAddon(Furnace furnace) throws InterruptedException
{
Addon addon = furnace.getAddonRegistry().getAddon(AddonId.from(WINDUP_UI_ADDON_NAME, WINDUP_OLD_VERSION));
Addons.waitUntilStarted(addon);
do
{
// We need to wait till furnace will process all the information changed in the directories.
Thread.sleep(500);
}
while (furnace.getAddonRegistry().getServices(WindupUpdateDistributionCommand.class).isUnsatisfied());
}
/**
* Uninstalls the current addon and installs the other one. This fails because we would need to replace all the
* dependencies as well, effectively, the whole Windup.
*/
private void installOldAddonVersion(String currentUiVersion)
{
RemoveRequest remove = manager.remove(AddonId.from(WINDUP_UI_ADDON_NAME, currentUiVersion));
remove.perform();
final AddonId olderAddonId = AddonId.from(WINDUP_UI_ADDON_NAME, WINDUP_OLD_VERSION);
log.info("Downgrading to " + olderAddonId + ". This may take a while to download.");
InstallRequest install = manager.install(olderAddonId);
install.perform();
}
/**
* Changes the org-jboss-windup-ui-windup-ui-* dir name to old version and rewrites the version in installed.xml. It
* is a hack to fool Furnace into thinking a new addon was installed.
*/
private void changeUiAddonVersion(File addonsDir, String currentUiVersion)
{
File currentAddonDir = new File(addonsDir,
"org-jboss-windup-ui-windup-ui-" + currentUiVersion.replaceAll("\\.", "-"));
File olderVersionAddonDir = new File(addonsDir,
"org-jboss-windup-ui-windup-ui-" + WINDUP_OLD_VERSION.replaceAll("\\.", "-"));
olderVersionAddonDir.mkdirs();
log.warning("Replacing the addon: \n " + currentAddonDir + "\n " + olderVersionAddonDir);
try
{
FileUtils.copyDirectory(currentAddonDir, olderVersionAddonDir);
changeUiAddonVersionInInstallXml(addonsDir.getPath());
FileUtils.deleteDirectory(currentAddonDir);
}
catch (IOException ex)
{
throw new RuntimeException("Failed replacing the addon: ", ex);
}
}
/**
* Reads the version of the windup-ui addon from addons/installed.xml .
*/
private String getInstalledAddonVersion(String addonsRootDir, String addonName)
{
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder;
try
{
String oldUiAddonVersion = "";
File installedXml = new File(addonsRootDir, "installed.xml");
if (!installedXml.exists())
throw new WindupException("installed.xml doesn't exist: " + installedXml.getAbsolutePath());
dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(installedXml);
// this does not work properly
Element documentElement = doc.getDocumentElement();
NodeList childNodes = documentElement.getElementsByTagName("addon");
for (int i = 0; i <= childNodes.getLength() - 1; i++)
{
Element item = (Element) childNodes.item(i);
if (item.getNodeName().equals("addon"))
{
String addonNameAttr = item.getAttribute("name");
if (addonNameAttr.equals(addonName))
{
oldUiAddonVersion = item.getAttribute("version");
return oldUiAddonVersion;
}
}
}
}
catch (ParserConfigurationException ex)
{
throw new RuntimeException("Failed parsing installed.xml: " + ex.getMessage(), ex);
}
catch (WindupException ex)
{
throw ex;
}
catch (Exception ex)
{
throw new RuntimeException("Unknown exception: " + ex.getMessage(), ex);
}
return null;
}
/**
* Writes the version WINDUP_OLD_VERSION into installed.xml in given dir.
*/
private void changeUiAddonVersionInInstallXml(String homeAddonsDirPath)
{
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder;
try
{
dBuilder = dbFactory.newDocumentBuilder();
File installedXml = new File(homeAddonsDirPath, "installed.xml");
Document doc = dBuilder.parse(installedXml);
// this does not work properly
Element documentElement = doc.getDocumentElement();
NodeList childNodes = documentElement.getElementsByTagName("addon");
for (int i = 0; i <= childNodes.getLength() - 1; i++)
{
Element item = (Element) childNodes.item(i);
if (item.getNodeName().equals("addon"))
{
String addonName = item.getAttribute("name");
if (addonName.equals(WINDUP_UI_ADDON_NAME))
{
item.setAttribute("version", WINDUP_OLD_VERSION);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(installedXml);
transformer.transform(source, result);
return;
}
}
}
}
catch (ParserConfigurationException e)
{
e.printStackTrace();
}
catch (Exception e)
{
System.out.println("Why?");
}
return;
}
}