/*******************************************************************************
* Copyright (c) 2004 Ferenc Hechler - ferenc_hechler@users.sourceforge.net
*
* This file is part of the Fat Jar Eclipse Plug-In
*
* The Fat Jar Eclipse Plug-In is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option) any later version.
*
* The Fat Jar Eclipse Plug-In 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Fat Jar Eclipse Plug-In;
* if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*******************************************************************************/
package net.sf.fjep.fatjar.builder;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import net.sf.fjep.utils.FileUtils;
/**
* This class generates the "One Jar".
*
* @author Ferenc Hechler
*/
public class OneJarBuilder {
// The name of the One-JAR distribution being used.
public static final String ONE_JAR_BOOT = "one-jar-boot-0.95.jar";
private String tempBuildDir = null;
private ArrayList fileSystemSources = null;
private ArrayList conflictResolvers = null;
private String onejarManifestText = "Manifest-Version: 1.0\r\nCreated-By: Fat Jar/One-JAR Eclipse Plug-In\r\nMain-Class: com.simontuffs.onejar.Boot\r\n\r\n";
private IFileSystemSource onejarHelperResource = null;
/**
* Use this class to generate a One Jar.
* 1. add multiple IFileSystemSources addSource().
* 2. Create the One Jar using build().
* @param tempBuildDir - temporary directory which will be
* created to collect data and removed after Fat Jar creation
* is finished
*/
public OneJarBuilder(String tempBuildDir) {
this.tempBuildDir = tempBuildDir;
fileSystemSources = new ArrayList();
conflictResolvers = new ArrayList();
}
/**
* add a File System Source to collection
* @param fileSource
*/
public void addSource(IFileSystemSource fileSource) {
if (!fileSystemSources.contains(fileSource))
fileSystemSources.add(fileSource);
}
/**
* add a Conflict Resolver to collection
* @param conflictResolver
*/
public void addConflictResolver(IConflictResolver conflictResolver) {
if (!conflictResolvers.contains(conflictResolver))
conflictResolvers.add(conflictResolver);
}
/**
* collect - pack - clean
*/
public void build(String oneJarFilename) {
clean();
collect();
pack(oneJarFilename);
clean();
}
/**
* collect all data added via addSource to the tempBuildDir
* in the order of adding.
* Order is only relevant for conflicts.<br>
* 1. copy all jars to build/lib<br>
* 2. copy all other files to build/mainjartmp<br>
* 3. jar build/mainjartmp to build/main/main.jar<br>
* 4. remove build/mainjartmp<br>
* 5. copy One-Jar Boot Files<br>
* 6. create One-Jar Manifest<br>
*/
private void collect() {
// 1. copy all jars to build/lib
File fLibFolder = new File(tempBuildDir, "lib");
fLibFolder.mkdirs();
for (Iterator iterator = fileSystemSources.iterator(); iterator.hasNext();) {
IFileSystemSource fileSystemSource = (IFileSystemSource) iterator.next();
if (fileSystemSource instanceof JARFileSystemSource) {
JARFileSystemSource jarSource = (JARFileSystemSource) fileSystemSource;
String sourceFilename = jarSource.getJarFilename();
File fSource = new File(sourceFilename);
File fDest = new File(fLibFolder, fSource.getName());
// TODO: handle conflicts for jars with same name (fDest exists)
FileUtils.copyFile(fSource, fDest);
}
}
// 2. copy all other files to build/mainjartmp
File fMainFolder = new File(tempBuildDir, "mainjartmp");
for (Iterator iterator = fileSystemSources.iterator(); iterator.hasNext();) {
IFileSystemSource fileSystemSource = (IFileSystemSource) iterator.next();
if (!(fileSystemSource instanceof JARFileSystemSource)) {
while (fileSystemSource.hasMoreElements()) {
IFileSystemElement fileSystemElement = (IFileSystemElement) fileSystemSource.nextElement();
String elementFolder = fileSystemElement.getFolder();
File folder = new File(fMainFolder, elementFolder);
FileUtils.mkDirs(folder);
if (!fileSystemElement.isFolder()) {
String elementName = fileSystemElement.getName();
File outputFile = new File(folder, elementName);
if (outputFile.exists())
resolveConflicts(outputFile, fileSystemElement);
else {
InputStream fileStream = fileSystemElement.getStream();
FileUtils.writeToFile(outputFile, fileStream);
}
}
}
}
}
// 3. jar build/main to build/lib/main.jar
String mainJarName = tempBuildDir + File.separatorChar + "main" + File.separatorChar + "main.jar";
IJarBuilder jb = new JarBuilder(mainJarName);
jb.add(new File(fMainFolder, "META-INF/MANIFEST.MF"), "META-INF/MANIFEST.MF");
IFileSystemSource mainSource = new NativeFileSystemSource(fMainFolder, "");
while (mainSource.hasMoreElements()) {
IFileSystemElement fse = mainSource.nextElement();
if (!fse.getFullName().replace('\\', '/').equals("META-INF/MANIFEST.MF")) {
jb.add(fse);
}
}
jb.close();
// 4. remove build/main
FileUtils.recursiveRm(fMainFolder);
// 5. copy One-Jar Boot Files
copyOneJARBootFiles();
// 5b copy helper resources
// TODO: extract FileSystemCopy(destdir) method
if (onejarHelperResource != null) {
while (onejarHelperResource.hasMoreElements()) {
IFileSystemElement fileSystemElement = onejarHelperResource.nextElement();
String elementFolder = fileSystemElement.getFolder();
File folder = new File(tempBuildDir, elementFolder);
FileUtils.mkDirs(folder);
if (!fileSystemElement.isFolder()) {
String elementName = fileSystemElement.getName();
File outputFile = new File(folder, elementName);
if (!outputFile.exists()) {
InputStream fileStream = fileSystemElement.getStream();
FileUtils.writeToFile(outputFile, fileStream);
}
}
}
}
// 6. create One-Jar Manifest
File fMetaInf = new File(tempBuildDir, "META-INF");
fMetaInf.mkdirs();
FileUtils.writeToFile(new File(fMetaInf, "MANIFEST.MF"), onejarManifestText);
}
private void copyOneJARBootFiles() {
// Put the OneJAR Boot files at the top of the tree.
JarInputStream jis = null;
try {
jis = new JarInputStream(OneJarBuilder.class.getResourceAsStream(ONE_JAR_BOOT));
JarEntry entry = (JarEntry)jis.getNextEntry();
while (entry != null) {
File dest = new File(tempBuildDir, entry.getName());
if (dest.getName().endsWith(".class") || entry.getName().startsWith("doc")) {
dest.getParentFile().mkdirs();
FileUtils.writeToFile(dest, jis, false);
}
entry = (JarEntry)jis.getNextEntry();
}
jis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* handle conflicts (multiple sources for same target output file.
* currently this function does nothing, the first file written wins.
* TODO: handle manifest.mf when merging is active.
* @param conflictOutputFile
* @param fileSystemElement
*/
private void resolveConflicts(File conflictOutputFile, IFileSystemElement fileSystemElement) {
boolean ok = false;
for (Iterator iterator = conflictResolvers.iterator(); iterator.hasNext();) {
IConflictResolver conflictResolver = (IConflictResolver) iterator.next();
ok = conflictResolver.handleConflict(conflictOutputFile, fileSystemElement);
if (ok)
break;
}
if (!ok) {
System.out.println("not resolving conflict in file " + fileSystemElement.getFolder() + File.separator + fileSystemElement.getName());
}
}
/**
* erase tempBuildDir
*/
private void clean() {
FileUtils.recursiveRm(new File(tempBuildDir));
}
/**
* jar all files in tempBuildDir to fatJarFilename
*/
private void pack(String jarName) {
IJarBuilder newJar = new JarBuilder(jarName);
NativeFileSystemSource source = new NativeFileSystemSource(new File(tempBuildDir), "");
while (source.hasMoreElements()) {
IFileSystemElement element = source.nextElement();
newJar.add(element);
}
newJar.close();
}
public void setOnejarManifestText(String onejarManifestText) {
this.onejarManifestText = onejarManifestText;
}
public void setOnejarHelperResource (IFileSystemSource onejarHelperResource ) {
this.onejarHelperResource = onejarHelperResource ;
}
}