package org.jboss.arquillian.container.common;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ArchivePath;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.Node;
import org.jboss.shrinkwrap.api.asset.ArchiveAsset;
/**
* This is a rip-off from ExplodedExporterDelegate. ;-)
*/
class FixedExplodedExporter {
private static final Logger log = Logger.getLogger(FixedExplodedExporter.class.getName());
private Archive archive;
private File outputDirectory;
private boolean explodeWars = true;
FixedExplodedExporter(Archive archive, File root) {
this.archive = archive;
this.outputDirectory = initializeOutputDirectory(root, archive.getName());
}
void setExplodeWars(boolean explodeWars) {
this.explodeWars = explodeWars;
}
File export() {
doExport();
return outputDirectory;
}
protected void doExport() {
if (log.isLoggable(Level.FINE)) {
log.fine("Exporting archive - " + archive.getName());
}
processArchive("", archive);
}
protected void processArchive(String relativePath, Archive archive) {
// Obtain the root
final Node rootNode = archive.get(ArchivePaths.root());
// Recursively process the root children
for (Node child : rootNode.getChildren()) {
processNode(relativePath, child);
}
}
protected boolean isWar(Node node) {
return (node.getPath().get().endsWith(".war"));
}
/**
* Recursive call to process all the node hierarchy
*
* @param node the node
*/
protected void processNode(final String relativePath, final Node node) {
final boolean isDirectory = (node.getAsset() == null);
final boolean explodeWar = explodeWars && isWar(node);
final ArchivePath path = node.getPath();
processNode(relativePath, path, node, isDirectory || explodeWar);
if (explodeWar) {
ArchiveAsset war = (ArchiveAsset) node.getAsset();
processArchive(relativePath + path.get(), war.getArchive());
} else {
Set<Node> children = node.getChildren();
for (Node child : children) {
processNode(relativePath, child);
}
}
}
protected void processNode(String relativePath, ArchivePath path, Node node, boolean isDirectory) {
// Get path to file
final String assetFilePath = path.get();
// Create a file for the asset
final File assetFile = new File(outputDirectory, relativePath + assetFilePath);
// Get the assets parent parent directory and make sure it exists
final File assetParent = assetFile.getParentFile();
if (!assetParent.exists()) {
if (!assetParent.mkdirs()) {
throw new IllegalArgumentException("Failed to write asset. Unable to create parent directory.");
}
}
// Handle directory and inner .war assets separately
try {
if (isDirectory) {
// If doesn't already exist
if (!assetFile.exists()) {
// Attempt a create
if (!assetFile.mkdirs()) {
// Some error in writing
throw new IllegalArgumentException("Failed to write directory: " + assetFile.getAbsolutePath());
}
}
}
// Only handle non-directory assets, otherwise the path is handled above
else {
try {
if (log.isLoggable(Level.FINE)) {
log.fine("Writing asset " + path.get() + " to " + assetFile.getAbsolutePath());
}
// Get the asset streams
final InputStream assetInputStream = node.getAsset().openStream();
final FileOutputStream assetFileOutputStream = new FileOutputStream(assetFile);
final BufferedOutputStream assetBufferedOutputStream = new BufferedOutputStream(assetFileOutputStream, 8192);
// Write contents
copyWithClose(assetInputStream, assetBufferedOutputStream);
} catch (final Exception e) {
// Provide a more detailed exception than the outer block
throw new IllegalArgumentException("Failed to write asset " + path + " to " + assetFile, e);
}
}
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new IllegalArgumentException("Unexpected error encountered in export of " + node, e);
}
}
private File initializeOutputDirectory(File baseDirectory, String directoryName) {
// Create output directory
final File outputDirectory = new File(baseDirectory, directoryName);
boolean mkdirs = outputDirectory.mkdirs();
boolean exists = outputDirectory.exists();
if (!mkdirs && !exists) {
throw new IllegalArgumentException(String.format("Unable to create archive output directory - %s [%s, %s]", outputDirectory, mkdirs, exists));
}
if (outputDirectory.isFile()) {
throw new IllegalArgumentException(String.format("Unable to export exploded directory to %s, it points to an existing file", outputDirectory.getAbsolutePath()));
}
return outputDirectory;
}
private static void copy(InputStream input, OutputStream output) throws IOException {
final byte[] buffer = new byte[4096];
int read;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
output.flush();
}
private static void copyWithClose(InputStream input, OutputStream output) throws IOException {
try {
copy(input, output);
} finally {
try {
input.close();
} catch (final IOException ignore) {
if (log.isLoggable(Level.FINER)) {
log.finer("Could not close stream due to: " + ignore.getMessage() + "; ignoring");
}
}
try {
output.close();
} catch (final IOException ignore) {
if (log.isLoggable(Level.FINER)) {
log.finer("Could not close stream due to: " + ignore.getMessage() + "; ignoring");
}
}
}
}
}