/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, 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.patching.tests;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import org.jboss.as.patching.HashUtils;
import org.jboss.as.patching.IoUtils;
import org.jboss.as.patching.installation.PatchableTarget;
import org.jboss.as.patching.runner.TestUtils;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.jboss.as.patching.runner.PatchUtils.BACKUP_EXT;
import static org.jboss.as.patching.runner.PatchUtils.JAR_EXT;
import static org.jboss.as.patching.runner.TestUtils.createModule0;
import static org.jboss.as.patching.runner.TestUtils.randomString;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.is;
/**
* @author Emanuel Muckenhuber
*/
public class PatchModuleInvalidationTestCase extends AbstractPatchingTest {
private static final Boolean INVALIDATION_ENABLED = Boolean.getBoolean("org.wildfly.patching.jar.invalidation");
private static final String MODULE_NAME = "org.jboss.test.module";
private static final String RESOURCE = "resource0.jar";
private static final TestUtils.ContentTask CONTENT_TASK = new TestUtils.ContentTask() {
@Override
public String[] writeContent(File target) throws IOException {
writeJar(new File(target, RESOURCE));
return new String[] { RESOURCE };
}
};
@BeforeClass
public static void checkValidationEnabled() {
Assume.assumeTrue(INVALIDATION_ENABLED);
}
@Test
public void test() throws Exception {
final PatchingTestBuilder test = createDefaultBuilder();
final File root = test.getRoot();
final File installation = new File(root, JBOSS_INSTALLATION);
final File moduleRoot = new File(installation, "modules/system/layers/base".replace('/', File.separatorChar));
final File module0 = createModule0(moduleRoot, MODULE_NAME, CONTENT_TASK);
final File resource = new File(module0, "main/resource0.jar".replace('/', File.separatorChar));
final File resourceBackup = new File(module0, "main/resource0.jar.patched".replace('/', File.separatorChar));
assertLoadable(resource);
final byte[] existingHash = HashUtils.hashFile(module0);
final byte[] resultingHash = Arrays.copyOf(existingHash, existingHash.length);
final PatchingTestStepBuilder oop1 = test.createStepBuilder();
oop1.setPatchId("oop1")
.oneOffPatchIdentity(PRODUCT_VERSION)
.oneOffPatchElement("base-oop1", "base", false)
.updateModule(MODULE_NAME, existingHash, resultingHash, CONTENT_TASK)
;
apply(oop1);
assertThat(resourceBackup.exists(), is(true));
assertThat(resource.exists(), is(false));
assertNotLoadable(resourceBackup);
// Module in patch oop1
final File resource1 = getModuleResource("base-oop1", MODULE_NAME);
assertLoadable(resource1);
final File resource1Backup = getBackupFile(resource1);
assertThat(resource1Backup.exists(), is(false));
assertThat(resource1.exists(), is(true));
final PatchingTestStepBuilder oop2 = test.createStepBuilder();
oop2.setPatchId("oop2")
.oneOffPatchIdentity(PRODUCT_VERSION)
.oneOffPatchElement("base-oop2", "base", false)
.updateModule(MODULE_NAME, resultingHash, null, CONTENT_TASK)
;
apply(oop2);
// Module in patch oop2
final File resource2 = getModuleResource("base-oop2", MODULE_NAME);
final File resource2Backup = getBackupFile(resource2);
assertThat(resource2Backup.exists(), is(false));
assertThat(resource2.exists(), is(true));
assertNotLoadable(resourceBackup);
assertThat(resource1Backup.exists(), is(true));
assertThat(resource1.exists(), is(false));
assertNotLoadable(resource1Backup);
assertLoadable(resource2);
final PatchingTestStepBuilder cp1 = test.createStepBuilder();
cp1.setPatchId("cp1")
.upgradeIdentity(PRODUCT_VERSION, PRODUCT_VERSION)
.upgradeElement("base-cp1", "base", false)
.updateModule(MODULE_NAME, existingHash, resultingHash, CONTENT_TASK)
;
apply(cp1);
// Module in patch cp1
final File resource3 = getModuleResource("base-cp1", MODULE_NAME);
final File resource3Backup = getBackupFile(resource3);
assertThat(resource3Backup.exists(), is(false));
assertThat(resource3.exists(), is(true));
assertNotLoadable(resourceBackup);
assertNotLoadable(resource1Backup);
assertThat(resource2Backup.exists(), is(true));
assertThat(resource2.exists(), is(false));
assertNotLoadable(resource2Backup);
assertLoadable(resource3);
final PatchingTestStepBuilder cp2 = test.createStepBuilder();
cp2.setPatchId("cp2")
.upgradeIdentity(PRODUCT_VERSION, PRODUCT_VERSION)
.upgradeElement("base-cp2", "base", false)
.updateModule(MODULE_NAME, resultingHash, null, CONTENT_TASK)
;
apply(cp2);
// Module in patch cp2
final File resource4 = getModuleResource("base-cp2", MODULE_NAME);
assertNotLoadable(resourceBackup);
assertNotLoadable(resource1Backup);
assertNotLoadable(resource2Backup);
assertThat(resource3Backup.exists(), is(true));
assertThat(resource3.exists(), is(false));
assertNotLoadable(resource3Backup);
assertLoadable(resource4);
rollback(cp2);
assertNotLoadable(resourceBackup);
assertNotLoadable(resource1Backup);
assertNotLoadable(resource2Backup);
assertThat(resource3Backup.exists(), is(false));
assertThat(resource3.exists(), is(true));
assertLoadable(resource3);
rollback(cp1);
assertNotLoadable(resourceBackup);
assertNotLoadable(resource1Backup);
assertThat(resource2Backup.exists(), is(false));
assertThat(resource2.exists(), is(true));
assertLoadable(resource2);
rollback(oop2);
assertNotLoadable(resourceBackup);
assertThat(resource1Backup.exists(), is(false));
assertThat(resource1.exists(), is(true));
assertLoadable(resource1);
rollback(oop1);
assertThat(resourceBackup.exists(), is(false));
assertThat(resource.exists(), is(true));
assertLoadable(resource);
}
File getModuleResource(final String patchID, final String moduleName) throws IOException {
return getModuleResource("base", patchID, moduleName);
}
File getModuleResource(final String layer, final String patchID, final String moduleName) throws IOException {
final PatchableTarget.TargetInfo info = getLayer(layer).loadTargetInfo();
final File root = info.getDirectoryStructure().getModulePatchDirectory(patchID);
final File moduleRoot = TestUtils.getModuleRoot(root, moduleName);
return new File(moduleRoot, RESOURCE);
}
static File writeJar(final File target) {
final JavaArchive archive = ShrinkWrap.create(JavaArchive.class)
.addClass(TestClass.class)
.add(new StringAsset(randomString()), "testResource")
.addManifest();
archive.as(ZipExporter.class).exportTo(target);
return target;
}
static void assertLoadable(final File jar) throws Exception {
final URL[] urls = new URL[] { jar.toURI().toURL() };
final URLClassLoader cl = new URLClassLoader(urls);
try {
Assert.assertNotNull(cl.getResource("testResource"));
final Class<?> clazz = cl.loadClass("org.jboss.as.patching.tests.TestClass");
final Constructor<?> constructor = clazz.getConstructor(String.class);
final Object instance = constructor.newInstance("test");
Assert.assertNotNull(instance);
} finally {
IoUtils.safeClose(cl);
}
}
static void assertNotLoadable(final File jar) throws Exception {
final URL[] urls = new URL[] { jar.toURI().toURL() };
final URLClassLoader cl = new URLClassLoader(urls, null);
try {
Assert.assertNull(cl.getResource("testResource"));
try {
cl.loadClass("org.jboss.as.patching.tests.TestClass");
Assert.fail("shouldn't be able to load the test class");
} catch (ClassNotFoundException ok) {
//
}
} finally {
IoUtils.safeClose(cl);
}
ZipFile file = null;
try {
file = new ZipFile(jar);
Assert.fail("should not be able to open" + jar);
} catch (ZipException expected) {
// ok
return;
} finally {
IoUtils.safeClose(file);
}
}
private File getBackupFile(File file) {
String fileName = file.getName();
if (fileName.endsWith(JAR_EXT)) {
return new File(file.getParentFile(), fileName.substring(0, fileName.length() - JAR_EXT.length()) + BACKUP_EXT);
}
return file;
}
}