/******************************************************************************* * 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.popup.actions; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.Vector; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarInputStream; import java.util.jar.JarOutputStream; import net.sf.fjep.fatjar.builder.IFileSystemElement; import net.sf.fjep.fatjar.builder.IFileSystemSource; import net.sf.fjep.fatjar.extensionpoints.IJarUtilFactory; import net.sf.fjep.fatjar.preferences.FatJarPreferencePage; import net.sf.fjep.fatjar.wizards.export.FJExpWizard; import net.sf.fjep.fatjar.wizards.export.JProjectConfiguration; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Preferences; import org.eclipse.jdt.core.IClasspathContainer; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.core.JavaModelManager; import org.eclipse.jface.action.IAction; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IActionDelegate; import org.eclipse.ui.IObjectActionDelegate; import org.eclipse.ui.IWorkbenchPart; import com.google.common.base.Strings; import thahn.java.agui.ide.eclipse.project.AguiConstants; import thahn.java.agui.ide.eclipse.project.BaseProjectHelper; import thahn.java.agui.ide.eclipse.wizard.AguiPlugin; public class BuildFatJar implements IObjectActionDelegate { private final static int PROGRESS_CLEAN = 50; private final static int PROGRESS_COLLECT = 1000; private final static int PROGRESS_PACK = 300; private final static char PATH_SEPARATOR = '.'; // hack for changing current properties file... public static String absPropertiesFilename = null; protected IStructuredSelection lastSelection = null; /** * Constructor for Action1. */ public BuildFatJar() { super(); } /** * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart) */ public void setActivePart(IAction action, IWorkbenchPart targetPart) { } /** * @see IActionDelegate#run(IAction) */ public void run(IAction action) { if ((lastSelection != null) && !lastSelection.isEmpty()) { Object sel = lastSelection.getFirstElement(); if (sel instanceof IJavaProject) { absPropertiesFilename = null; if (action.getId().equals("net.sf.fjep.fatjar.buildFatJar")) runBuildFatJar(lastSelection.toList()); else System.err.println("unknown action-id for BuildFatJar: " + action.getId()); } else if (sel instanceof IFile) { for(Iterator iter = lastSelection.iterator();iter.hasNext();) { Object ifile = iter.next(); if(ifile instanceof IFile) buildFatJarConditional(action, ifile); } } } } private void buildFatJarConditional(IAction action, Object sel) { IFile settings = (IFile) sel; String projectName = settings.getProject().getName(); IJavaProject jproject = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProject(projectName); absPropertiesFilename = settings.getLocation().toOSString(); if (action.getId().equals("net.sf.fjep.fatjar.buildFatJarFromSettings")) { List jplist = new ArrayList(); jplist.add(jproject); runBuildFatJar(jplist); } else if (action.getId().equals("net.sf.fjep.fatjar.quickBuildFatJarFromSettings")) { // TODO: Add collections for quick build runQuickBuildFatJarFromSettings(jproject); } else { System.err.println("unknown action-id for BuildFatJar: " + action.getId()); } } /** * @param jproject * @param settings */ private void runQuickBuildFatJarFromSettings(IJavaProject jproject) { try { doQuickBuildFatJar(jproject); } catch (Exception e) { e.printStackTrace(); Shell shell = new Shell(); MessageDialog.openInformation(shell, "Fat Jar Plug-In ERROR", e .getMessage()); } } protected void runBuildFatJar(List jprojects) { boolean ok = false; try { ok = doBuildFatJar(jprojects); } catch (Exception e) { e.printStackTrace(); Shell shell = new Shell(); MessageDialog.openInformation(shell, "Fat Jar Plug-In ERROR", e .getMessage()); } } public void runBuildConfiguredFatJar(IJavaProject jproject, Properties props, Set excludes, ArrayList includes) { // Create a progress bar Shell shell = new Shell(); ProgressMonitorDialog progressmonitordialog = new ProgressMonitorDialog( shell); progressmonitordialog.open(); IProgressMonitor iprogressmonitor = progressmonitordialog .getProgressMonitor(); iprogressmonitor.beginTask("Build Fat jar", PROGRESS_CLEAN + PROGRESS_COLLECT + PROGRESS_PACK + PROGRESS_CLEAN); // Make One-JAR? boolean onejar = new Boolean(props .getProperty(AguiPlugin.ONEJAR_CHECKBOX)).booleanValue(); String expand = props.getProperty(AguiPlugin.ONEJAR_EXPAND); String jarfile = null; try { saveConfig(jproject, props); doCleanFatJar(jproject, iprogressmonitor); doCollectFatJar(jproject, iprogressmonitor, excludes, includes, onejar, expand); jarfile = doPackFatJar(jproject, iprogressmonitor); doCleanFatJar(jproject, iprogressmonitor); try { jproject.getProject().refreshLocal(IResource.DEPTH_ONE, null); } catch (CoreException e) { e.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); MessageDialog.openInformation(shell, "Fat Jar Plug-In ERROR", e .getMessage()); } progressmonitordialog.close(); MessageDialog.openInformation(shell, "Fat Jar Plug-In", "built " + jarfile); } private boolean doBuildFatJar(List jprojects) { boolean ok = false; Shell shell = new Shell(); JProjectConfiguration jprojectConf = new JProjectConfiguration( jprojects); jprojectConf.setPropertiesFilename(absPropertiesFilename); FJExpWizard wizard = new FJExpWizard(jprojectConf); ok = wizard.execute(shell); if (ok) { try { for (Iterator iter = jprojects.iterator(); iter.hasNext();) { IJavaProject jproject = (IJavaProject) iter.next(); jproject.getProject().refreshLocal(IResource.DEPTH_ONE, null); } } catch (CoreException e) { e.printStackTrace(); } } return ok; } // TODO: beta1 - change to launchconfig public void doQuickBuildFatJar(IJavaProject jproject) { Properties props = getFatJarProperties(jproject); Set excludes = getExcludes(jproject, props.getProperty("excludes")); ArrayList includes = getIncludes(jproject, props .getProperty("includes")); // Create a progress bar Shell shell = new Shell(); ProgressMonitorDialog progressmonitordialog = new ProgressMonitorDialog( shell); progressmonitordialog.open(); IProgressMonitor iprogressmonitor = progressmonitordialog .getProgressMonitor(); iprogressmonitor.beginTask("Quick Build Fat jar", PROGRESS_CLEAN + PROGRESS_COLLECT + PROGRESS_PACK + PROGRESS_CLEAN); // Make One-JAR? boolean onejar = new Boolean(props .getProperty(AguiPlugin.ONEJAR_CHECKBOX)).booleanValue(); String expand = props.getProperty(AguiPlugin.ONEJAR_EXPAND); String jarfile = null; try { // saveConfig(jproject, props); doCleanFatJar(jproject, iprogressmonitor); doCollectFatJar(jproject, iprogressmonitor, excludes, includes, onejar, expand); jarfile = doPackFatJar(jproject, iprogressmonitor); doCleanFatJar(jproject, iprogressmonitor); try { jproject.getProject().refreshLocal(IResource.DEPTH_ONE, null); } catch (CoreException e) { e.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); MessageDialog.openInformation(shell, "Fat Jar Plug-In ERROR", e .getMessage()); } progressmonitordialog.close(); MessageDialog.openInformation(shell, "Fat Jar Plug-In", "built " + jarfile); // testExtensionPoints(jarfile); } /** * @param jarfile */ private void testExtensionPoints(String jarfile) { try { System.out .println("dumping contents in jar using extension net.sf.fjep.fatjar.fatjarJarUtil"); System.out.println("--- " + jarfile + " ---"); IExtensionRegistry reg = Platform.getExtensionRegistry(); IExtensionPoint exp = reg .getExtensionPoint("net.sf.fjep.fatjar.jarutil"); IExtension[] extensions = exp.getExtensions(); IConfigurationElement[] elements = extensions[0] .getConfigurationElements(); IJarUtilFactory ju = (IJarUtilFactory) elements[0] .createExecutableExtension("class"); IFileSystemSource fss = ju.createJARFileSystemSource(jarfile, ""); while (fss.hasMoreElements()) { IFileSystemElement fse = fss.nextElement(); System.out.println(fse.getFullName()); } System.out.println("--- finished ---"); } catch (Exception e) { e.printStackTrace(); } } /** * @param jproject * @param property * @return */ private ArrayList getIncludes(IJavaProject jproject, String includeString) { ArrayList result = new ArrayList(); IWorkspaceRoot root = jproject.getProject().getWorkspace().getRoot(); if ((includeString != null) && !includeString.trim().equals("")) { String[] includes = includeString.split("[;]"); for (int i = 0; i < includes.length; i++) { String include = includes[i]; if (include.startsWith("<inc|")) include = include.replaceFirst("[<]inc[|](.*)[>]", "/$1|") .replaceAll("[~]", "").replace('/', File.separatorChar); String absPath = absProjectPath(root, include); result.add(absPath); } } return result; } /** * @param jproject * @param property * @return */ private Set getExcludes(IJavaProject jproject, String excludeString) { Set result = new HashSet(); IWorkspaceRoot root = jproject.getProject().getWorkspace().getRoot(); if ((excludeString != null) && !excludeString.trim().equals("")) { String[] excludes = excludeString.split("[;]"); for (int i = 0; i < excludes.length; i++) { String exclude = excludes[i]; String absPath = exclude; if (exclude.startsWith("<po|")) { String javaProjectName = exclude.replaceFirst( "[<]po[|](.*)[>](.*)", "$1"); exclude = exclude.replaceFirst("[<]po[|](.*)[>](.*)", "$2") .replaceAll("[~]", "").replace('/', File.separatorChar); try { IJavaProject javaProject = JavaModelManager .getJavaModelManager().getJavaModel() .getJavaProject(javaProjectName); if (javaProject != null) { String projectOutput = javaProject .getOutputLocation().toOSString(); exclude = projectOutput + File.separatorChar + exclude; } } catch (JavaModelException e) { e.printStackTrace(); } absPath = absProjectPath(root, exclude); } else if (exclude.startsWith("<cl|")) { exclude = exclude.replaceFirst("[<]cl[|](.*)[>]", "$1/") .replaceAll("[~]", "").replace('/', File.separatorChar); absPath = exclude; } else if (exclude.startsWith("<jar|")) { exclude = exclude.replaceFirst("[<]jar[|](.*)[>]", "$1"); // absolute path is unknown here, but the check for excudes // recognises jars to exclude without path absPath = exclude; } if (absPath.endsWith(File.separator)) { absPath = absPath.substring(0, absPath.length() - 1); } result.add(absPath); } } return result; } private void saveConfig(IJavaProject jproject, Properties props) { boolean ok = true; try { String propertiesFile = getPropertiesFilename(jproject); File f = new File(propertiesFile); if (f.exists() && f.canRead()) { // check for changes try { InputStream in = new FileInputStream(f); Properties oldProps = new Properties(); oldProps.load(in); in.close(); if (oldProps.equals(props)) { ok = false; } } catch (Exception ignore) { } // allow check out of properties if readonly if (ok) { String projectDir = getProjectDir(jproject); if (propertiesFile.startsWith(projectDir)) { String relFile = propertiesFile.substring(projectDir .length() + 1); IFile editFile = jproject.getProject().getFile(relFile); if (editFile != null) { IStatus status = AguiPlugin .askFileWriteAccess(editFile); ok = (status == null) || status.isOK(); if (!ok) { Shell shell = new Shell(); MessageDialog.openInformation(shell, "Fat Jar Plug-In Warning", "could not save current settings: " + status.toString()); } } } } } if (ok) { OutputStream out = new FileOutputStream(f); props.store(out, "Fat Jar Configuration File"); out.flush(); out.close(); try { jproject.getProject().refreshLocal(IResource.DEPTH_ONE, null); } catch (CoreException e) { e.printStackTrace(); } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static Properties getFatJarProperties(IJavaProject jproject) { Properties props = new Properties(); try { String propertiesFile = getPropertiesFilename(jproject); File f = new File(propertiesFile); if (!f.exists()) { props.setProperty("jarname.isextern", "false"); props.setProperty("jarname", getJarName(jproject)); props.setProperty("manifest.file", "<createnew>"); props.setProperty("manifest.mainclass", ""); props.setProperty("manifest.classpath", ""); props.setProperty("manifest.mergeall", getManifestMergeAll(jproject)); props.setProperty("includes", "notyetsupported"); props.setProperty("excludes", "notyetsupported"); } else { InputStream in = new FileInputStream(f); props.load(in); in.close(); } } catch (Exception e) { e.printStackTrace(); } return props; } private String doCollectFatJar(IJavaProject jproject, IProgressMonitor iprogressmonitor, Set excludes, ArrayList includes, boolean onejar, String onejarexpand) throws IOException { Shell shell = new Shell(); String jartype = onejar ? "One-JAR" : "Fat Jar"; iprogressmonitor.subTask("Collect " + jartype + " - reading classpath"); Vector childProjects = new Vector(); Vector jarFiles = new Vector(); Vector classesDirs = new Vector(); Vector libDirs = new Vector(); Vector libNames = new Vector(); getChildProjects(jproject, childProjects, false); for (int i = childProjects.size() - 1; i >= 0; i--) { IJavaProject jChildProject = (IJavaProject) childProjects.get(i); if (onejar) { // Ferenc: libNames have to be updated for every new entry in // libdirs. // dirname is not unique, so using enumeration of childproject // name String childProjectName = jChildProject.getElementName(); int current = libDirs.size(); getClassPathJars(jChildProject, jarFiles, libDirs, true); for (int j = 1; j <= libDirs.size() - current; j++) { String libDir = (String) libDirs.get(j - 1); int start = libDir.indexOf(childProjectName); if (start >= 0) { start += childProjectName.length() + 1; String libName = libDir.substring(start).replace( File.separatorChar, PATH_SEPARATOR); libNames.add(libName); } else { libNames.add(childProjectName + PATH_SEPARATOR + Integer.toString(i)); } } getClassesDir(jChildProject, libDirs); libNames.add(childProjectName); } else { getClassPathJars(jChildProject, jarFiles, classesDirs, true); getClassesDir(jChildProject, classesDirs); } } if (onejar) { int current = libDirs.size(); String projectName = jproject.getElementName(); getClassPathJars(jproject, jarFiles, libDirs, false); for (int i = 1; i <= libDirs.size() - current; i++) { String libDir = (String) libDirs.get(i - 1); int start = libDir.indexOf(projectName); if (start >= 0) { start += projectName.length() + 1; String libName = libDir.substring(start).replace( File.separatorChar, PATH_SEPARATOR); libNames.add(libName); } else { libNames.add(projectName + PATH_SEPARATOR + Integer.toString(i)); } } } else { getClassPathJars(jproject, jarFiles, classesDirs, false); } getClassesDir(jproject, classesDirs); String projectDir = getProjectDir(jproject); String projectName = getProjectName(jproject); String tempdir = configGetTempBuildDir(jproject); boolean mergeManifests = configGetManifestMergeAll(jproject); mkdir(tempdir); ManifestData manifestData = null; if (mergeManifests) manifestData = new ManifestData(); iprogressmonitor.worked(PROGRESS_COLLECT * 1 / 20); for (int i = 0; i < jarFiles.size(); i++) { String jarFile = (String) jarFiles.get(i); String jarname = File.separatorChar + jarFile; jarname = jarname .substring(jarname.lastIndexOf(File.separatorChar) + 1); iprogressmonitor.subTask("Collect " + jartype + " - extracting " + jarname); if (!checkFileInList(jarFile, excludes) && !checkFileInList(jarname, excludes)) extractJar(jarFile, tempdir, manifestData, onejar); iprogressmonitor.worked(PROGRESS_COLLECT * 15 / 20 / jarFiles.size()); } for (int i = 0; i < classesDirs.size(); i++) { String classesDir = (String) classesDirs.get(i); iprogressmonitor.subTask("Collect " + jartype + " - copying class files " + classesDir); if (!onejar) { copyFiles(classesDir, tempdir, excludes, jproject); } else { // there is only one element in classesDirs if onejar is true, // so this code is only executed once. String mainClass = configGetMainClass(jproject); if (mainClass == null || mainClass.length() == 0) throw new IOException( "Main-Class manifest entry cannot be missing in a One-JAR application"); String classPath = configGetClassPath(jproject); copyJar(classesDir, tempdir, excludes, mainClass, classPath, "main/main.jar", jproject); copyOneJARBootFiles(tempdir); } iprogressmonitor.worked(PROGRESS_COLLECT * 2 / 20 / classesDirs.size()); } // One-JAR supporting libraries taken from other projects classes. for (int i = 0; i < libDirs.size(); i++) { String classesDir = (String) libDirs.get(i); copyJar(classesDir, tempdir, excludes, null, null, "lib/" + libNames.get(i) + ".jar", jproject); } for (int i = 0; i < includes.size(); i++) { String include = (String) includes.get(i); String[] basedir_relpath = include.split("[|]"); String baseDir = basedir_relpath[0]; String relPath = ""; if (basedir_relpath.length > 1) relPath = File.separatorChar + basedir_relpath[1]; iprogressmonitor.subTask("Collect " + jartype + " - including resource files " + relPath); String src = baseDir + relPath; String dest = tempdir + relPath; File fsrc = new File(src); File fdest = new File(dest); fdest.getParentFile().mkdirs(); recursiveCopyFiles(fsrc, fdest); } iprogressmonitor.worked(PROGRESS_COLLECT * 1 / 20); iprogressmonitor.subTask("Collect " + jartype + " creating manifest"); mkdir(tempdir + File.separator + "META-INF"); if (manifestData == null) manifestData = new ManifestData(); String manifestFile = configGetManifestFile(jproject); if (manifestFile.equals("<createnew>")) { manifestData.clearMainSection(); manifestData.addMainSectionLine("Manifest-Version: 1.0"); if (onejar) { manifestData .addMainSectionLine("Created-By: Fat Jar/One-JAR Eclipse Plug-In"); manifestData .addMainSectionLine("Main-Class: com.simontuffs.onejar.Boot"); if (onejarexpand.length() > 0) manifestData.addMainSectionLine("One-Jar-Expand: " + onejarexpand); } else { manifestData .addMainSectionLine("Created-By: Fat Jar Eclipse Plug-In"); String mainClass = configGetMainClass(jproject); if ((mainClass != null) && !mainClass.equals("")) manifestData.addMainSectionLine("Main-Class: " + mainClass); String classPath = configGetClassPath(jproject); if ((classPath != null) && !classPath.equals("")) manifestData.addMainSectionLine("Class-Path: " + classPath); } } else { manifestData.addFile(projectDir + File.separator + manifestFile); } String manifestText = manifestData.toString(); writeTextToFile(new File(tempdir + File.separator + "META-INF" + File.separator + "MANIFEST.MF"), manifestText); iprogressmonitor.worked(PROGRESS_COLLECT * 1 / 20); return tempdir; } private boolean checkFileInList(String filename, Set files) { boolean result = false; if (files != null) { if (files.contains(filename)) result = true; else { File f = new File(filename); try { String abs = f.getCanonicalPath(); if (files.contains(abs)) result = true; } catch (IOException e) { } } } return result; } public FJTree buildTree(IJavaProject jproject) { String projectName = jproject.getElementName(); FJTree rootNode = new FJTree(null, FJTree.NT_ROOT, "Fat Jar Tree for Project " + projectName, jproject, FJTree.CS_CHECKED); Vector childProjects = new Vector(); Vector jarFiles = new Vector(); Vector classesDirs = new Vector(); getClassPathJars(jproject, jarFiles, classesDirs, false); getClassesDir(jproject, classesDirs); String classesDir = (String) classesDirs.lastElement(); rootNode.addChild(FJTree.NT_PROJECT_OUTPUT, "Project '" + jproject.getElementName() + "' output", new File(classesDir), FJTree.CS_CHECKED); for (int i = 0; i < classesDirs.size() - 1; i++) { classesDir = (String) classesDirs.get(i); rootNode.addChild(FJTree.NT_CLASSES, "Classes '" + classesDir + "'", new File(classesDir), FJTree.CS_CHECKED); } getChildProjects(jproject, childProjects, false); for (int i = 0; i < childProjects.size(); i++) { IJavaProject jChildProject = (IJavaProject) childProjects.get(i); String childProjectName = jChildProject.getElementName(); classesDirs.clear(); getClassPathJars(jChildProject, jarFiles, classesDirs, true); getClassesDir(jChildProject, classesDirs); classesDir = (String) classesDirs.lastElement(); rootNode.addChild(FJTree.NT_PROJECT_OUTPUT, "Project '" + childProjectName + "' output", new File(classesDir), FJTree.CS_CHECKED); for (int j = 0; j < classesDirs.size() - 1; j++) { classesDir = (String) classesDirs.get(j); rootNode.addChild(FJTree.NT_CLASSES, "Classes '" + classesDir + "'", new File(classesDir), FJTree.CS_CHECKED); } } for (int i = 0; i < jarFiles.size(); i++) { int checkd = FJTree.CS_CHECKED; String jarFile = (String) jarFiles.get(i); if (jarFile.endsWith(AguiConstants.AGUI_SDK_JAR)) { checkd = FJTree.CS_UNCHECKED; } String jarname = File.separatorChar + jarFile; jarname = jarname.substring(jarname.lastIndexOf(File.separatorChar) + 1); rootNode.addChild(FJTree.NT_JAR, jarname, new File(jarFile), checkd); } // include AguiManifest.xml String aguiManifestPath = BaseProjectHelper.getManifest(jproject.getProject()).getRawLocation().toFile().getAbsolutePath(); rootNode.addChild(FJTree.NT_FILE, AguiConstants.AGUI_MANIFEST, new File(aguiManifestPath), FJTree.CS_CHECKED); // res String aguiResPath = BaseProjectHelper.getResFolder(jproject.getProject()).getRawLocation().toFile().getAbsolutePath(); rootNode.addChild(FJTree.NT_DIR, AguiConstants.FD_RES, new File(aguiResPath), FJTree.CS_CHECKED); return rootNode; } private void getChildProjects(IJavaProject jproject, Vector projects, boolean exportedOnly) { IClasspathEntry[] cpes = jproject.readRawClasspath(); if (cpes != null) { for (int i = 0; i < cpes.length; i++) { IClasspathEntry cpe = JavaCore .getResolvedClasspathEntry(cpes[i]); if (cpe == null) { System.err.println("Error: cpes[" + i + "]=" + cpes[i] + " does not resolve"); continue; } int kind = cpe.getEntryKind(); String name = relPath(cpe.getPath()); if (kind == IClasspathEntry.CPE_CONTAINER) { try { IClasspathContainer container = JavaCore .getClasspathContainer(cpe.getPath(), jproject); if ((container.getKind() == IClasspathContainer.K_APPLICATION) || (container.getKind() == IClasspathContainer.K_SYSTEM)) { IClasspathEntry[] cpes2 = container .getClasspathEntries(); for (int j = 0; j < cpes2.length; j++) { IClasspathEntry cpe2 = cpes2[j]; int kind2 = cpe2.getEntryKind(); String name2 = relPath(cpe2.getPath()); if (name2 == null) { System.err .println("invalid classpath entry: " + cpe2.toString()); } else { if (kind2 == IClasspathEntry.CPE_PROJECT) { if (!exportedOnly || cpe2.isExported()) { if (!projects.contains(name2)) { IJavaProject jChildProject2 = jproject .getJavaModel() .getJavaProject(name2); projects.add(jChildProject2); getChildProjects( jChildProject2, projects, true); } } } } } } } catch (JavaModelException e) { } } else if (kind == IClasspathEntry.CPE_PROJECT) { if (name == null) { System.err.println("invalid classpath entry: " + cpe.toString()); } else { if (!exportedOnly || cpe.isExported()) { if (!projects.contains(name)) { IJavaProject jChildProject = jproject .getJavaModel().getJavaProject(name); projects.add(jChildProject); getChildProjects(jChildProject, projects, true); } } } } } } } /** * do some security checking, that not the whole harddisc is scratched. if * tempDir is not a subdirectory of the project dir, then use * "TempFatjarBuildDir" */ private String configGetTempBuildDir(IJavaProject jproject) { String projectDir = getProjectDir(jproject); String absProject = projectDir + File.separatorChar; String buildDir = "TempFatjarBuildDir_erase_me"; String result = absProject + buildDir; return result; } private boolean configGetJarNameIsExtern(IJavaProject jproject) { Properties props = getProperties(jproject); String result = props.getProperty("jarname.isextern", "false"); return result.equalsIgnoreCase("true"); } private String configGetJarName(IJavaProject jproject) { Properties props = getProperties(jproject); String result = props.getProperty("jarname", getJarName(jproject)); if (!configGetJarNameIsExtern(jproject)) result = getProjectDir(jproject) + File.separator + result; return result; } private String configGetManifestFile(IJavaProject jproject) { Properties props = getProperties(jproject); String result = props.getProperty("manifest.file", "<createnew>"); return result; } private String configGetMainClass(IJavaProject jproject) { Properties props = getProperties(jproject); String result = props.getProperty("manifest.mainclass", ""); return result; } private String configGetClassPath(IJavaProject jproject) { Properties props = getProperties(jproject); String result = props.getProperty("manifest.classpath", ""); return result; } private boolean configGetManifestMergeAll(IJavaProject jproject) { Properties props = getProperties(jproject); String result = props.getProperty("manifest.mergeall", "true"); return result.equalsIgnoreCase("true"); } private Properties getProperties(IJavaProject jproject) { Properties props = new Properties(); try { String propertiesFile = getPropertiesFilename(jproject); File f = new File(propertiesFile); String msg; if (f.exists()) { InputStream in = new FileInputStream(f); props.load(in); in.close(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return props; } private String doPackFatJar(IJavaProject jproject, IProgressMonitor iprogressmonitor) { String tempdir = configGetTempBuildDir(jproject); String jarName = configGetJarName(jproject); boolean ok = checkWritable(jproject, jarName); if (!ok) { jarName = "(no write access)"; } else { iprogressmonitor.subTask("Pack Fat Jar - " + jarName); packJar(jarName, tempdir); iprogressmonitor.worked(PROGRESS_PACK); } return jarName; } private boolean checkWritable(IJavaProject jproject, String filename) { boolean result = true; String projectDir = getProjectDir(jproject) + File.separator; if (filename.startsWith(projectDir)) { String relFilename = filename.substring(projectDir.length()); IFile editFile = jproject.getProject().getFile(relFilename); IStatus status = AguiPlugin.askFileWriteAccess(editFile); if ((status != null) && !status.isOK()) { result = false; Shell shell = new Shell(); MessageDialog.openInformation(shell, "Fat Jar Plug-In ERROR", "no write acces for output jar '" + relFilename + "': " + status.toString()); } } return result; } /** * 50 progress */ private String doCleanFatJar(IJavaProject jproject, IProgressMonitor iprogressMonitor) { String tempdir = configGetTempBuildDir(jproject); iprogressMonitor.subTask("Clean Fat Jar - remove " + tempdir); recursiveRm(new File(tempdir)); iprogressMonitor.worked(50); return tempdir; } public static String getJarName(IJavaProject jproject) { IPreferenceStore store = AguiPlugin.getDefault().getPreferenceStore(); String result = store.getString(FatJarPreferencePage.P_JARNAME); return result.replaceAll("[<]project[>]", getProjectName(jproject)); } public static String getManifestMergeAll(IJavaProject jproject) { IPreferenceStore store = AguiPlugin.getDefault().getPreferenceStore(); boolean result = store.getBoolean(FatJarPreferencePage.P_MERGEMANIFEST); return (result ? "true" : "false"); } public static String getManifestRemoveSigners(IJavaProject jproject) { IPreferenceStore store = AguiPlugin.getDefault().getPreferenceStore(); boolean result = store.getBoolean(FatJarPreferencePage.P_REMOVESIGNERS); return (result ? "true" : "false"); } public static boolean getScmAutoCheckout() { IPreferenceStore store = AguiPlugin.getDefault().getPreferenceStore(); boolean result = store .getBoolean(FatJarPreferencePage.P_SCMAUTOCHECKOUT); return result; } private static String getRelPropertiesFilename(IJavaProject jproject) { Preferences prefs = AguiPlugin.getDefault().getPluginPreferences(); String result = prefs.getString(FatJarPreferencePage.P_CONFIGFILE); if (Strings.isNullOrEmpty(result)) { return result = getProjectName(jproject) + "-agui.properties"; } return result.replaceAll("[<]project[>]", getProjectName(jproject)); } public static String getPropertiesFilename(IJavaProject jproject) { String result = absPropertiesFilename; if (result == null) result = getProjectDir(jproject) + File.separator + getRelPropertiesFilename(jproject); return result; } private void recursiveJarFilesExclude(JarOutputStream jarro, File rootDir, String filename, Set excludes) { try { File fsrc; if (filename == null) fsrc = rootDir; else fsrc = new File(rootDir, filename); if (!checkFileInList(fsrc, excludes)) { if (fsrc.isDirectory()) { if (filename == null) filename = ""; else { filename += File.separator; JarEntry entry = new JarEntry(filename.replace('\\', '/')); jarro.putNextEntry(entry); jarro.closeEntry(); } String[] filenames = fsrc.list(); for (int i = 0; i < filenames.length; i++) { recursiveJarFilesExclude(jarro, rootDir, filename + filenames[i], excludes); } } else { JarEntry entry = new JarEntry(filename.replace('\\', '/')); entry.setSize(fsrc.length()); entry.setTime(fsrc.lastModified()); jarro.putNextEntry(entry); FileInputStream in = new FileInputStream(fsrc); byte[] buffer = new byte[4096]; int cnt = in.read(buffer); while (cnt > 0) { jarro.write(buffer, 0, cnt); cnt = in.read(buffer); } jarro.closeEntry(); in.close(); } } } catch (IOException e) { e.printStackTrace(); } } private void recursiveJarFiles(JarOutputStream jarro, File rootDir, String filename) { try { File fsrc; if (filename == null) fsrc = rootDir; else fsrc = new File(rootDir, filename); if (fsrc.isDirectory()) { if (filename == null) filename = ""; else { filename += File.separator; JarEntry entry = new JarEntry(filename.replace('\\', '/')); jarro.putNextEntry(entry); jarro.closeEntry(); } String[] filenames = fsrc.list(); for (int i = 0; i < filenames.length; i++) { recursiveJarFiles(jarro, rootDir, filename + filenames[i]); } } else { JarEntry entry = new JarEntry(filename.replace('\\', '/')); entry.setSize(fsrc.length()); entry.setTime(fsrc.lastModified()); jarro.putNextEntry(entry); FileInputStream in = new FileInputStream(fsrc); byte[] buffer = new byte[4096]; int cnt = in.read(buffer); while (cnt > 0) { jarro.write(buffer, 0, cnt); cnt = in.read(buffer); } jarro.closeEntry(); in.close(); } } catch (IOException e) { e.printStackTrace(); } } private void packJar(String jarName, String tempdir) { try { OutputStream out = new FileOutputStream(jarName); JarOutputStream jarro = new JarOutputStream(out); recursiveJarFiles(jarro, new File(tempdir), null); jarro.flush(); jarro.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private static String getProjectName(IJavaProject jproject) { IProject project = jproject.getProject(); String projectName = project.getName(); return projectName; } private void extractJar(String jarfilename, String destdir, ManifestData md, boolean onejar) { try { if (!onejar) { JarFile jarFile = new JarFile(jarfilename); Enumeration enumeration = jarFile.entries(); while (enumeration.hasMoreElements()) { JarEntry jarEntry = (JarEntry) enumeration.nextElement(); String entryName = jarEntry.getName(); if (jarEntry.isDirectory()) { mkdir(destdir + File.separator + entryName); } else { mkParentDirs(destdir + File.separator + entryName); InputStream in = jarFile.getInputStream(jarEntry); writeToFile(in, destdir + File.separator + entryName); in.close(); if ((md != null) && (entryName .equalsIgnoreCase("META-INF/MANIFEST.MF"))) { md.addFile(destdir + File.separator + entryName); } } } jarFile.close(); } else { // One-JAR just copies the JAR files in to a "lib" directory. FileInputStream input = new FileInputStream(new File( jarfilename)); String output = destdir + "/lib/" + new File(jarfilename).getName(); ; mkParentDirs(output); writeToFile(input, output); } } catch (IOException e) { e.printStackTrace(); } } private void mkParentDirs(String filename) { String normFilename = filename.replace('\\', File.separatorChar) .replace('/', File.separatorChar); int pos = normFilename.lastIndexOf(File.separatorChar); String dir = normFilename.substring(0, pos); File f = new File(dir); f.mkdirs(); } private void writeTextToFile(File f, String text) { try { OutputStream out = new FileOutputStream(f); out.write(text.getBytes()); out.flush(); out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private void writeToFile(InputStream in, String filename) { writeToFile(in, new File(filename)); } private void writeToFile(InputStream in, File fdest) { try { FileOutputStream out = new FileOutputStream(fdest); byte[] buffer = new byte[4096]; int cnt = in.read(buffer); while (cnt > 0) { out.write(buffer, 0, cnt); cnt = in.read(buffer); } out.flush(); out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private void copyJar(String classesDir, String tempdir, Set excludes, String mainclass, String classpath, String jar, IJavaProject jproject) throws IOException { // Copy classes into a "main.jar" file under "main" subdirectory. // stop if dir is excluded if (checkFileInList(classesDir, excludes)) return; // exclude tempdir if it is beyond classes if (checkSubDir(new File(classesDir), new File(tempdir))) { try { String abs = new File(tempdir).getCanonicalPath(); if (excludes == null) excludes = new HashSet(); excludes.add(abs); } catch (IOException e) { } } // exclude output jarfile if it is beyond srcDir to avoid doubling with // each build if (checkSubDir(new File(classesDir), new File( configGetJarName(jproject)))) { try { String abs = new File(configGetJarName(jproject)) .getCanonicalPath(); if (excludes == null) excludes = new HashSet(); excludes.add(abs); } catch (IOException e) { } } File maindir = new File(tempdir, jar); maindir.getParentFile().mkdirs(); JarOutputStream output = new JarOutputStream(new FileOutputStream( maindir)); // Write the manifest which would have been present in the original // Fat-Jar file. JarEntry man = new JarEntry("META-INF/MANIFEST.MF"); output.putNextEntry(man); ManifestData manifestData = new ManifestData(); manifestData .addMainSectionLine("Created-By: Fat Jar/One-JAR Eclipse Plug-In"); manifestData.addMainSectionLine("Main-Class: " + mainclass); if ((classpath != null) && !classpath.equals("")) manifestData.addMainSectionLine("Class-Path: " + classpath); output.write(manifestData.toString().getBytes()); if ((excludes != null) || (excludes.size() > 0)) recursiveJarFilesExclude(output, new File(classesDir), null, excludes); else recursiveJarFiles(output, new File(classesDir), null); output.close(); } private void copyOneJARBootFiles(String tempdir) throws IOException { // Put the OneJAR Boot files at the top of the tree. JarInputStream jis = new JarInputStream(AguiPlugin.class .getResourceAsStream(AguiPlugin.ONE_JAR_BOOT)); JarEntry entry = (JarEntry) jis.getNextEntry(); while (entry != null) { File dest = new File(tempdir, entry.getName()); if (dest.getName().endsWith(".class") || entry.getName().startsWith("doc")) { dest.getParentFile().mkdirs(); writeToFile(jis, dest); } entry = (JarEntry) jis.getNextEntry(); } } private void copyFiles(String srcDir, String destDir, Set excludes, IJavaProject jproject) { if (!checkFileInList(srcDir, excludes)) { File fsrc = new File(srcDir); File fdest = new File(destDir); // exclude destDir if it is beyond srcDir to avoid an endless // recursion if (checkSubDir(fsrc, fdest)) { try { String abs = fdest.getCanonicalPath(); if (excludes == null) excludes = new HashSet(); excludes.add(abs); } catch (IOException e) { } } // exclude output jarfile if it is beyond srcDir to avoid doubling // with each build if (checkSubDir(fsrc, new File(configGetJarName(jproject)))) { try { String abs = new File(configGetJarName(jproject)) .getCanonicalPath(); if (excludes == null) excludes = new HashSet(); excludes.add(abs); } catch (IOException e) { } } if ((excludes != null) || (excludes.size() > 0)) recursiveCopyFilesExclude(fsrc, fdest, excludes); else recursiveCopyFiles(fsrc, fdest); } } /** * @return true if fchild is descendant of fparent, false on any error */ private boolean checkSubDir(File fparent, File fchild) { boolean result = false; try { result = fchild.getCanonicalPath().startsWith( fparent.getCanonicalPath()); } catch (IOException e) { e.printStackTrace(); } return result; } private void recursiveCopyFiles(File fsrc, File fdest) { if (fsrc.isDirectory()) { fdest.mkdirs(); String[] filenames = fsrc.list(); for (int i = 0; i < filenames.length; i++) { File fchildsrc = new File(fsrc, filenames[i]); File fchilddest = new File(fdest, filenames[i]); recursiveCopyFiles(fchildsrc, fchilddest); } } else { copyFile(fsrc, fdest); } } /** * copy all files recursive from fsrc to fdest, skip all (src-)files in * exclude */ private void recursiveCopyFilesExclude(File fsrc, File fdest, Set excludes) { if (!checkFileInList(fsrc, excludes)) { if (fsrc.isDirectory()) { fdest.mkdirs(); String[] filenames = fsrc.list(); for (int i = 0; i < filenames.length; i++) { File fchildsrc = new File(fsrc, filenames[i]); File fchilddest = new File(fdest, filenames[i]); recursiveCopyFilesExclude(fchildsrc, fchilddest, excludes); } } else { copyFile(fsrc, fdest); } } } private boolean checkFileInList(File file, Set files) { boolean result = false; String abs = null; try { abs = file.getCanonicalPath(); if (files.contains(abs)) result = true; } catch (IOException e) { } return result; } private void copyFile(String src, String dest) { copyFile(new File(src), new File(dest)); } private void copyFile(File fsrc, File fdest) { try { FileInputStream in = new FileInputStream(fsrc); writeToFile(in, fdest); in.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private void mkdir(String dir) { File f = new File(dir); f.mkdirs(); } private void recursiveRm(File f) { if (f.isDirectory()) { String[] filenames = f.list(); for (int i = 0; i < filenames.length; i++) recursiveRm(new File(f, filenames[i])); f.delete(); } else { f.delete(); } } /** * Add all jars and class-folders referenced by jproject to jarfiles / * classesDirs. If exportedOnly is true, then only jars/class-folders which * are marked as exported will be added. * * JRE_LIB (.../jre/lib/rt.jar) is ignored and not added to jarfiles * * @param jproject * @param jarfiles * @param classesDirs * @param exportedOnly */ private void getClassPathJars(IJavaProject jproject, Vector jarfiles, Vector classesDirs, boolean exportedOnly) { IProject project = jproject.getProject(); IWorkspace workspace = project.getWorkspace(); IWorkspaceRoot workspaceRoot = workspace.getRoot(); String rootDir = absPath(workspaceRoot.getLocation()); IClasspathEntry[] cpes = jproject.readRawClasspath(); // cpes = jproject.getResolvedClasspath(true); if (cpes != null) { for (int i = 0; i < cpes.length; i++) { IClasspathEntry cpe = JavaCore .getResolvedClasspathEntry(cpes[i]); if ((cpe != null) && (!exportedOnly || cpe.isExported())) { int kind = cpe.getEntryKind(); String dir = relPath(cpe.getPath()); if (kind == IClasspathEntry.CPE_CONTAINER) { try { IClasspathContainer container = JavaCore .getClasspathContainer(cpe.getPath(), jproject); if ((container.getKind() == IClasspathContainer.K_APPLICATION) || (container.getKind() == IClasspathContainer.K_SYSTEM)) { IClasspathEntry[] cpes2 = container .getClasspathEntries(); for (int j = 0; j < cpes2.length; j++) { IClasspathEntry cpe2 = cpes2[j]; int kind2 = cpe2.getEntryKind(); String dir2 = relPath(cpe2.getPath()); String jar2 = absOrProjectPath( workspaceRoot, dir2); if (jar2 == null) { System.err .println("invalid classpath entry: " + cpe2.toString()); } else { File f2 = new File(jar2); if (f2.isDirectory()) { if (!classesDirs.contains(jar2)) { classesDirs.add(jar2); } } else { // assume jar file if (!jarfiles.contains(jar2)) { jarfiles.add(jar2); } } } } } } catch (JavaModelException e) { } } else if (kind == IClasspathEntry.CPE_LIBRARY) { String jar = absOrProjectPath(workspaceRoot, dir); if (jar == null) { System.err.println("invalid classpath entry: " + cpe.toString()); } else { // ignore JRE_LIB if (!jar.replace(File.separatorChar, '/') .toLowerCase().endsWith("/jre/lib/rt.jar")) { File f = new File(jar); if (f.isDirectory()) { if (!classesDirs.contains(jar)) { classesDirs.add(jar); } } else { // assume jar file if (!jarfiles.contains(jar)) { jarfiles.add(jar); } } } } } } } } } public String absOrProjectPath(IWorkspaceRoot workspaceRoot, String absOrProjectpath) { String result = absOrProjectpath; try { File f = new File(absOrProjectpath); if (!f.exists()) { result = absProjectPath(workspaceRoot, absOrProjectpath); } } catch (Exception e) { } return result; } /** * get absolute path for /project/xyz by resolving "project" location */ public static String absProjectPath(IWorkspaceRoot workspaceRoot, String projectpath) { int n = projectpath.indexOf(File.separatorChar, 1); String jarProjectName; String jarProjectPath; if (n != -1) { jarProjectName = projectpath.substring(1, n); jarProjectPath = projectpath.substring(n); } else { jarProjectName = projectpath.substring(1); jarProjectPath = ""; } IProject jarProject = workspaceRoot.getProject(jarProjectName); String jarProjectRoot = absPath(jarProject.getLocation()); String result = null; if (jarProjectRoot != null) result = jarProjectRoot + jarProjectPath; return result; } private void getClassesDir(IJavaProject jproject, Vector classesDirs) { IProject project = jproject.getProject(); IWorkspaceRoot workspaceRoot = project.getWorkspace().getRoot(); String outputDir = null; try { outputDir = absProjectPath(workspaceRoot, relPath(jproject .getOutputLocation())); } catch (JavaModelException e) { e.printStackTrace(); } if (!classesDirs.contains(outputDir)) classesDirs.add(outputDir); } public static String getProjectDir(IJavaProject jproject) { IProject project = jproject.getProject(); String projectDir = absPath(project.getLocation()); return projectDir; } /** * convert IPath to absolute string path * * @param path * @return */ private static String absPath(IPath path) { String result = null; if (path != null) { File f = path.toFile(); try { result = f.getCanonicalPath(); } catch (IOException e) { e.printStackTrace(); } } return result; } /** * convert IPath to relative string path * * @param path * @return */ private String relPath(IPath path) { String result = null; File f = path.toFile(); result = f.getPath(); return result; } /** * @see IActionDelegate#selectionChanged(IAction, ISelection) */ public void selectionChanged(IAction action, ISelection selection) { lastSelection = null; if ((selection != null) && !selection.isEmpty()) { lastSelection = (IStructuredSelection) selection; } } }