/*
* 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 org.jboss.as.patching.Constants.BASE;
import static org.jboss.as.patching.IoUtils.mkdir;
import static org.jboss.as.patching.IoUtils.safeClose;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import org.jboss.as.patching.Constants;
import org.jboss.as.patching.DirectoryStructure;
import org.jboss.as.patching.HashUtils;
import org.jboss.as.patching.installation.PatchableTarget;
import org.jboss.as.patching.metadata.ModuleItem;
/**
* @author Emanuel Muckenhuber
*/
public final class PatchUtils {
public static final String JAR_EXT = ".jar";
public static final String BACKUP_EXT = ".jar.patched";
public static String readRef(final Properties properties, final String name) {
final String ref = (String) properties.get(name);
if(ref == null) {
return Constants.BASE;
}
return ref;
}
public static List<String> readRefs(final Properties properties) {
return readRefs(properties, Constants.PATCHES);
}
public static List<String> readRefs(final Properties properties, final String property) {
String layersProp = (String) properties.get(property);
if (layersProp == null || (layersProp = layersProp.trim()).length() == 0) {
return Collections.emptyList();
} else {
final String[] names = layersProp.split(",");
final List<String> patches = new ArrayList<String>();
for (final String name : names) {
patches.add(name);
}
return Collections.unmodifiableList(patches);
}
}
public static String asString(final List<String> values) {
final StringBuilder builder = new StringBuilder();
for (final String value : values) {
builder.append(value);
builder.append(',');
}
if (builder.length() > 0) {
builder.setLength(builder.length() - 1);
}
return builder.toString();
}
public static String readRef(final File file) throws IOException {
if(! file.exists()) {
return Constants.BASE;
}
final InputStream is = new FileInputStream(file);
try {
return readRef(is);
} finally {
safeClose(is);
}
}
public static List<String> readRefs(final File file) throws IOException {
if(! file.exists()) {
return Collections.emptyList();
}
final InputStream is = new FileInputStream(file);
try {
return readRefs(is);
} finally {
safeClose(is);
}
}
static String readRef(final InputStream is) throws IOException {
final StringBuffer buffer = new StringBuffer();
readLine(is, buffer);
return buffer.toString();
}
static List<String> readRefs(final InputStream is) throws IOException {
final List<String> refs = new ArrayList<String>();
final StringBuffer buffer = new StringBuffer();
do {
if(buffer.length() > 0) {
final String ref = buffer.toString().trim();
if(ref.length() > 0) {
refs.add(ref);
}
}
} while(readLine(is, buffer));
return refs;
}
public static void writeRef(final File file, final String ref) throws IOException {
mkdir(file.getParentFile());
final OutputStream os = new FileOutputStream(file);
try {
writeLine(os, ref);
os.flush();
os.close();
} finally {
safeClose(os);
}
}
public static void writeRefs(final File file, final List<String> refs, boolean append) throws IOException {
mkdir(file.getParentFile());
final OutputStream os = new FileOutputStream(file, append);
try {
writeRefs(os, refs);
os.flush();
os.close();
} finally {
safeClose(os);
}
}
public static void writeRefs(final File file, final List<String> refs) throws IOException {
writeRefs(file, refs, false);
}
static void writeRefs(final OutputStream os, final List<String> refs) throws IOException {
for(final String ref : refs) {
writeLine(os, ref);
}
}
static void writeLine(final OutputStream os, final String s) throws IOException {
os.write(s.getBytes(StandardCharsets.UTF_8));
os.write('\n');
}
static boolean readLine(InputStream is, StringBuffer buffer) throws IOException {
buffer.setLength(0);
int c;
for(;;) {
c = is.read();
switch(c) {
case '\t':
case '\r':
break;
case -1: return false;
case '\n': return true;
default: buffer.append((char) c);
}
}
}
// FIXME do we need to i18nize the timestamp?
static String generateTimestamp() {
return DateFormat.getInstance().format(new Date());
}
static File[] getModulePath(final DirectoryStructure structure, final PatchableTarget.TargetInfo info) {
final List<File> path = new ArrayList<File>();
final List<String> patches = info.getPatchIDs();
for (final String patch : patches) {
path.add(structure.getModulePatchDirectory(patch));
}
final String ref = info.getCumulativePatchID();
if (!BASE.equals(ref)) {
path.add(structure.getModulePatchDirectory(ref));
}
path.add(structure.getModuleRoot());
return path.toArray(new File[path.size()]);
}
static File[] getBundlePath(final DirectoryStructure structure, final PatchableTarget.TargetInfo info) {
final List<String> patches = info.getPatchIDs();
final List<File> path = new ArrayList<File>();
for (final String patch : patches) {
path.add(structure.getBundlesPatchDirectory(patch));
}
final String ref = info.getCumulativePatchID();
if (!BASE.equals(ref)) {
path.add(structure.getBundlesPatchDirectory(ref));
}
path.add(structure.getBundleRepositoryRoot());
return path.toArray(new File[path.size()]);
}
public static void writeProperties(final File file, final Properties properties) throws IOException {
final OutputStream os = new FileOutputStream(file);
try {
final Writer writer = new OutputStreamWriter(os, StandardCharsets.UTF_8);
properties.store(writer, "read only");
writer.close();
} finally {
safeClose(os);
}
}
public static Properties loadProperties(final File file) throws IOException {
if (! file.exists()) {
return new Properties();
}
Reader reader = null;
try {
reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
final Properties props = new Properties();
props.load(reader);
return props;
} finally {
safeClose(reader);
}
}
public static File getRenamedFileName(final File file) {
String fileName = file.getName();
if (fileName.endsWith(BACKUP_EXT)) {
return new File(file.getParentFile(), fileName.substring(0, fileName.length() - BACKUP_EXT.length()) + JAR_EXT);
} else if (fileName.endsWith(JAR_EXT)) {
return new File(file.getParentFile(), fileName.substring(0, fileName.length() - JAR_EXT.length()) + BACKUP_EXT);
}
return file;
}
public static byte[] getAbsentModuleContent(final ModuleItem item) {
final StringBuilder builder = new StringBuilder(128);
builder.append("<?xml version='1.0' encoding='UTF-8'?>\n<module-absent xmlns=\"urn:jboss:module:1.2\"");
builder.append(" name=\"").append(item.getName()).append("\"");
builder.append(" slot=\"").append(item.getSlot()).append("\"");
builder.append(" />\n");
return builder.toString().getBytes(StandardCharsets.UTF_8);
}
public static byte[] getAbsentModuleContentHash(final ModuleItem item) throws IOException {
final byte[] content = getAbsentModuleContent(item);
return HashUtils.hashBytes(content);
}
}