/* * JBoss, Home of Professional Open Source. * Copyright 2012, 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.runner; import static java.lang.String.format; import static java.util.UUID.randomUUID; import static org.jboss.as.patching.Constants.APP_CLIENT; import static org.jboss.as.patching.Constants.BASE; import static org.jboss.as.patching.Constants.BUNDLES; import static org.jboss.as.patching.Constants.DOMAIN; import static org.jboss.as.patching.Constants.INSTALLATION_METADATA; import static org.jboss.as.patching.Constants.LAYERS; import static org.jboss.as.patching.Constants.METADATA; import static org.jboss.as.patching.Constants.MODULES; import static org.jboss.as.patching.Constants.PATCHES; import static org.jboss.as.patching.Constants.STANDALONE; import static org.jboss.as.patching.Constants.SYSTEM; import static org.jboss.as.patching.IoUtils.mkdir; import static org.jboss.as.patching.IoUtils.newFile; import static org.jboss.as.patching.IoUtils.safeClose; import static org.jboss.as.patching.logging.PatchLogger.ROOT_LOGGER; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.Properties; import java.util.jar.Attributes.Name; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import org.jboss.as.patching.Constants; import org.jboss.as.patching.DirectoryStructure; import org.jboss.as.patching.ZipUtils; import org.jboss.as.patching.installation.InstalledImage; import org.jboss.as.patching.installation.PatchableTarget; import org.jboss.as.patching.metadata.BundledPatch; import org.jboss.as.patching.metadata.Patch; import org.jboss.as.patching.metadata.PatchBundleXml; import org.jboss.as.patching.metadata.PatchXml; import org.jboss.as.protocol.StreamUtils; /** * @author <a href="http://jmesnil.net/">Jeff Mesnil</a> (c) 2012, Red Hat Inc */ public class TestUtils { public static String randomString() { return randomUUID().toString(); } public static void tree(File dir) { if (!ROOT_LOGGER.isTraceEnabled()){ return; } StringBuilder out = new StringBuilder(); out.append(dir.getParentFile().getAbsolutePath()).append("\n"); tree0(out, dir, 1, " "); ROOT_LOGGER.trace(out.toString()); } private static void tree0(StringBuilder out, File dir, int indent, String tab) { StringBuilder shift = new StringBuilder(); for (int i = 0; i < indent; i++) { shift.append(tab); } out.append(shift + dir.getName() + "\n"); for (File child : dir.listFiles()) { if (child.isDirectory()) { tree0(out, child, indent + 1, tab); } else { out.append(shift + tab + child.getName() + "\n"); } } } public static File touch(File baseDir, String... segments) throws IOException { File f = baseDir; for (String segment : segments) { f = new File(f, segment); } f.getParentFile().mkdirs(); f.createNewFile(); return f; } public static void dump(File f, String content) throws IOException { try (final OutputStream os = new FileOutputStream(f)){ os.write(content.getBytes(StandardCharsets.UTF_8)); } } public static File createModuleXmlFile(File mainDir, String moduleSpec, String... resources) throws IOException { final int c1 = moduleSpec.lastIndexOf(':'); final String name; final String slot; if (c1 != -1) { name = moduleSpec.substring(0, c1); slot = moduleSpec.substring(c1 + 1); } else { name = moduleSpec; slot = "main"; } StringBuilder content = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); content.append(format("<module xmlns=\"urn:jboss:module:1.2\" name=\"%s\" slot=\"%s\">\n", name, slot)); content.append(" <resources>\n"); content.append(" <resource-root path=\".\"/>\n"); for (String resource : resources) { content.append(format(" <resource-root path=\"%s\"/>\n", resource)); } content.append(" </resources>\n"); content.append("</module>\n"); ROOT_LOGGER.trace(content); File moduleXMLFile = touch(mainDir, "module.xml"); dump(moduleXMLFile, content.toString()); return moduleXMLFile; } public static File createModule0(final File baseDir, final String moduleName, final String... resourcesContents) throws IOException { final ContentTask task = new ContentTask() { @Override public String[] writeContent(File mainDir) throws IOException { String[] resourceFileNames = new String[resourcesContents.length]; for (int i = 0; i < resourcesContents.length; i++) { String content = resourcesContents[i]; File f = File.createTempFile("test", i + ".tmp", mainDir); String fileName = f.getName(); resourceFileNames[i] = fileName; dump(f, content); } return resourceFileNames; } }; return createModule0(baseDir, moduleName, task); } public static File createModule0(final File baseDir, final String moduleName, final ContentTask task) throws IOException { final File main = createModuleRoot(baseDir, moduleName); final String[] resources = task.writeContent(main); createModuleXmlFile(main, moduleName, resources); return main.getParentFile(); } public static File createModule1(File baseDir, String moduleName, String... resourceFileNames) throws IOException { File mainDir = createModuleRoot(baseDir, moduleName); createModuleXmlFile(mainDir, moduleName, resourceFileNames); return mainDir.getParentFile(); } public static File createModuleRoot(File baseDir, String moduleSpec) throws IOException { final File dir = getModuleRoot(baseDir, moduleSpec); if (!dir.mkdirs() && !dir.exists()) { throw new IOException("failed to create " + dir.getAbsolutePath()); } return dir; } public static File getModuleRoot(final File baseDir, final String moduleSpec) { final int c1 = moduleSpec.lastIndexOf(':'); final String name; final String slot; if (c1 != -1) { name = moduleSpec.substring(0, c1); slot = moduleSpec.substring(c1 + 1); } else { name = moduleSpec; slot = "main"; } // assert slot.equals(ModuleItem.MAIN_SLOT); // update to support other slots too final String[] segments = name.split("\\."); assert segments.length > 0; File dir = baseDir; for (String segment : segments) { dir = new File(dir, segment); } dir = new File(dir, slot); return dir; } public static void createPatchXMLFile(File dir, Patch patch) throws Exception { createPatchXMLFile(dir, patch, false); } public static void createPatchXMLFile(File dir, Patch patch, boolean logContent) throws Exception { Path patchXMLfile = dir.toPath().resolve("patch.xml"); try (Writer fos = Files.newBufferedWriter(patchXMLfile, StandardCharsets.UTF_8)){ PatchXml.marshal(fos, patch); } if(logContent) { final byte[] bytes = Files.readAllBytes(dir.toPath().resolve("patch.xml")); System.out.println(new String(bytes, StandardCharsets.UTF_8)); } } public static void createPatchBundleXMLFile(File dir, final List<BundledPatch.BundledPatchEntry> patches) throws Exception { File bundleXMLFile = new File(dir, "patches.xml"); FileOutputStream fos = new FileOutputStream(bundleXMLFile); try { PatchBundleXml.marshal(fos, new BundledPatch() { @Override public List<BundledPatchEntry> getPatches() { return patches; } }); } finally { safeClose(fos); } } public static File createZippedPatchFile(File sourceDir, String zipFileName) { //tree(sourceDir); File zipFile = new File(sourceDir.getParent(), zipFileName + ".zip"); ZipUtils.zip(sourceDir, zipFile); return zipFile; } /** * Creates (a part of) the distribution on the filesystem necessary to the run the tests. * * @param env the directory structure to be created * @param identity the identity name * @param productName release name * @param productVersion release version * @return the bin directory * @throws Exception if anything goes wrong */ public static File createInstalledImage(DirectoryStructure env, String identity, String productName, String productVersion) throws Exception { // start from a base installation // with a file in it File binDir = mkdir(env.getInstalledImage().getJbossHome(), "bin"); // create product.conf File productConf = new File(binDir, "product.conf"); assertTrue("Failed to create product.conf", productConf.createNewFile()); Properties props = new Properties(); props.setProperty("slot", identity); try (final Writer writer = Files.newBufferedWriter(productConf.toPath(), StandardCharsets.UTF_8)) { props.store(writer, null); } // create the product module final File modulesDir = newFile(env.getInstalledImage().getModulesDir(), SYSTEM, LAYERS, BASE); if(!modulesDir.exists()) { modulesDir.mkdirs(); } final File moduleDir = TestUtils.createModule1(modulesDir, "org.jboss.as.product:" + identity, "product.jar"); final Manifest manifest = new Manifest(); manifest.getMainAttributes().putValue(Name.MANIFEST_VERSION.toString(), "xxx"); manifest.getMainAttributes().putValue("JBoss-Product-Release-Name", productName); manifest.getMainAttributes().putValue("JBoss-Product-Release-Version", productVersion); final File moduleJar = new File(new File(moduleDir, identity), "product.jar"); JarOutputStream jar = null; try { jar = new JarOutputStream(new FileOutputStream(moduleJar), manifest); jar.flush(); } finally { StreamUtils.safeClose(jar); } return binDir; } public static File[] getModuleRoot(final PatchableTarget target) { try { return PatchUtils.getModulePath(target.getDirectoryStructure(), target.loadTargetInfo()); } catch (IOException e) { throw new RuntimeException(e); } } /** * Create the legacy patch environment based on the default layout. * * @param jbossHome the $JBOSS_HOME * @return the patch environment * @deprecated see {@linkplain org.jboss.as.patching.installation.InstallationManager} */ @Deprecated public static DirectoryStructure createLegacyTestStructure(final File jbossHome) { final File appClient = new File(jbossHome, APP_CLIENT); final File bundles = new File(jbossHome, BUNDLES); final File domain = new File(jbossHome, DOMAIN); final File modules = new File(jbossHome, MODULES); final File installation = new File(jbossHome, Constants.INSTALLATION); final File patches = new File(modules, PATCHES); final File standalone = new File(jbossHome, STANDALONE); return new LegacyDirectoryStructure(new InstalledImage() { @Override public File getJbossHome() { return jbossHome; } @Override public File getBundlesDir() { return bundles; } @Override public File getModulesDir() { return modules; } @Override public File getInstallationMetadata() { return installation; } @Override public File getLayersConf() { return new File(getModulesDir(), Constants.LAYERS_CONF); } @Override public File getPatchesDir() { return patches; } @Override public File getPatchHistoryDir(String patchId) { return newFile(getInstallationMetadata(), PATCHES, patchId); } @Override public File getAppClientDir() { return appClient; } @Override public File getDomainDir() { return domain; } @Override public File getStandaloneDir() { return standalone; } }); } public interface ContentTask { /** * Write the content. * * @param target the target file * @return the created resources * @throws IOException for any error */ String[] writeContent(File target) throws IOException; } static class LegacyDirectoryStructure extends DirectoryStructure { private final InstalledImage image; LegacyDirectoryStructure(final InstalledImage image) { this.image = image; } @Override public InstalledImage getInstalledImage() { return image; } public File getPatchesMetadata() { return new File(getInstalledImage().getPatchesDir(), METADATA); } @Override public File getInstallationInfo() { return new File(getPatchesMetadata(), INSTALLATION_METADATA); } public File getPatchDirectory(final String patchId) { return new File(getInstalledImage().getPatchesDir(), patchId); } @Override public File getBundlesPatchDirectory(final String patchId) { return new File(getPatchDirectory(patchId), BUNDLES); } @Override public File getModulePatchDirectory(final String patchId) { return new File(getPatchDirectory(patchId), MODULES); } @Override public File getBundleRepositoryRoot() { return getInstalledImage().getBundlesDir(); } @Override public File getModuleRoot() { return getInstalledImage().getModulesDir(); } } }