[izpack-changes] r1907 - izpack-src/trunk/src/lib/com/izforge/izpack/installer
noreply at berlios.de
noreply at berlios.de
Fri Nov 16 15:20:58 CET 2007
Author: dreil
Date: 2007-11-16 15:20:55 +0100 (Fri, 16 Nov 2007)
New Revision: 1907
Added:
izpack-src/trunk/src/lib/com/izforge/izpack/installer/UnpackerBase.java
Modified:
izpack-src/trunk/src/lib/com/izforge/izpack/installer/MultiVolumeUnpacker.java
izpack-src/trunk/src/lib/com/izforge/izpack/installer/Unpacker.java
Log:
Refactored unpacker classes
Modified: izpack-src/trunk/src/lib/com/izforge/izpack/installer/MultiVolumeUnpacker.java
===================================================================
--- izpack-src/trunk/src/lib/com/izforge/izpack/installer/MultiVolumeUnpacker.java 2007-11-16 13:42:26 UTC (rev 1906)
+++ izpack-src/trunk/src/lib/com/izforge/izpack/installer/MultiVolumeUnpacker.java 2007-11-16 14:20:55 UTC (rev 1907)
@@ -18,7 +18,6 @@
package com.izforge.izpack.installer;
import java.awt.Component;
-import java.io.BufferedOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
@@ -29,25 +28,12 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Properties;
-import java.util.Stack;
-import java.util.TreeSet;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-import java.util.zip.ZipOutputStream;
import javax.swing.JOptionPane;
-import org.apache.regexp.RE;
-import org.apache.regexp.RECompiler;
-import org.apache.regexp.RESyntaxException;
-
import com.izforge.izpack.ExecutableFile;
-import com.izforge.izpack.LocaleDatabase;
import com.izforge.izpack.Pack;
import com.izforge.izpack.PackFile;
import com.izforge.izpack.ParsableFile;
@@ -59,227 +45,26 @@
import com.izforge.izpack.io.FileSpanningOutputStream;
import com.izforge.izpack.io.VolumeNotFoundException;
import com.izforge.izpack.panels.NextMediaDialog;
-import com.izforge.izpack.rules.RulesEngine;
import com.izforge.izpack.util.AbstractUIHandler;
import com.izforge.izpack.util.AbstractUIProgressHandler;
import com.izforge.izpack.util.Debug;
import com.izforge.izpack.util.FileExecutor;
import com.izforge.izpack.util.IoHelper;
import com.izforge.izpack.util.OsConstraint;
-import com.izforge.izpack.util.VariableSubstitutor;
/**
- * Unpacker class.
+ * Unpacker class for a multi volume installation.
*
* @author Dennis Reil
*/
-public class MultiVolumeUnpacker implements IUnpacker
-{
-
- /** The installdata. */
- private AutomatedInstallData idata;
-
- /** The installer listener. */
- private AbstractUIProgressHandler handler;
-
- /** The uninstallation data. */
- private UninstallData udata;
-
- /** The variables substitutor. */
- private VariableSubstitutor vs;
-
- /** The instances of the unpacker objects. */
- private static HashMap instances = new HashMap();
-
- /** The absolute path of the installation. (NOT the canonical!) */
- private File absolute_installpath;
-
- /** The packs locale database. */
- private LocaleDatabase langpack = null;
-
- /** Interrupt flag if global interrupt is desired. */
- private static boolean interruptDesired = false;
-
- /** Do not perform a interrupt call. */
- private static boolean discardInterrupt = false;
-
- /** The name of the XML file that specifies the panel langpack */
- private static final String LANG_FILE_NAME = "packsLang.xml";
-
- public static final String ALIVE = "alive";
-
- public static final String INTERRUPT = "doInterrupt";
-
- public static final String INTERRUPTED = "interruppted";
-
- /** The result of the operation. */
- private boolean result = true;
-
- private RulesEngine rules;
-
- /**
- * The constructor.
- *
- * @param idata The installation data.
- * @param handler The installation progress handler.
- */
+public class MultiVolumeUnpacker extends UnpackerBase
+{
public MultiVolumeUnpacker(AutomatedInstallData idata, AbstractUIProgressHandler handler)
- {
- try
- {
- String resource = LANG_FILE_NAME + "_" + idata.localeISO3;
- this.langpack = new LocaleDatabase(ResourceManager.getInstance().getInputStream(
- resource));
- }
- catch (Throwable exception)
- {}
-
- this.idata = idata;
- this.handler = handler;
-
- // Initialize the variable substitutor
- vs = new VariableSubstitutor(idata.getVariables());
- }
-
- /**
- * Returns a copy of the active unpacker instances.
- *
- * @return a copy of active unpacker instances
- */
- public static HashMap getRunningInstances()
{
- synchronized (instances)
- { // Return a shallow copy to prevent a
- // ConcurrentModificationException.
- return (HashMap) (instances.clone());
- }
+ super(idata, handler);
}
- /**
- * Adds this to the map of all existent instances of Unpacker.
- */
- private void addToInstances()
- {
- synchronized (instances)
- {
- instances.put(this, ALIVE);
- }
- }
-
- /**
- * Removes this from the map of all existent instances of Unpacker.
- */
- private void removeFromInstances()
- {
- synchronized (instances)
- {
- instances.remove(this);
- }
- }
-
- /**
- * Initiate interrupt of all alive Unpacker. This method does not interrupt the Unpacker objects
- * else it sets only the interrupt flag for the Unpacker objects. The dispatching of interrupt
- * will be performed by the Unpacker objects self.
- */
- private static void setInterruptAll()
- {
- synchronized (instances)
- {
- Iterator iter = instances.keySet().iterator();
- while (iter.hasNext())
- {
- Object key = iter.next();
- if (instances.get(key).equals(ALIVE))
- {
- instances.put(key, INTERRUPT);
- }
- }
- // Set global flag to allow detection of it in other classes.
- // Do not set it to thread because an exec will then be stoped.
- setInterruptDesired(true);
- }
- }
-
- /**
- * Initiate interrupt of all alive Unpacker and waits until all Unpacker are interrupted or the
- * wait time has arrived. If the doNotInterrupt flag in InstallerListener is set to true, the
- * interrupt will be discarded.
- *
- * @param waitTime wait time in millisecounds
- * @return true if the interrupt will be performed, false if the interrupt will be discarded
- */
- public static boolean interruptAll(long waitTime)
- {
- long t0 = System.currentTimeMillis();
- if (isDiscardInterrupt()) return (false);
- setInterruptAll();
- while (!isInterruptReady())
- {
- if (System.currentTimeMillis() - t0 > waitTime) return (true);
- try
- {
- Thread.sleep(100);
- }
- catch (InterruptedException e)
- {}
- }
- return (true);
- }
-
- private static boolean isInterruptReady()
- {
- synchronized (instances)
- {
- Iterator iter = instances.keySet().iterator();
- while (iter.hasNext())
- {
- Object key = iter.next();
- if (!instances.get(key).equals(INTERRUPTED)) return (false);
- }
- return (true);
- }
-
- }
-
- /**
- * Sets the interrupt flag for this Unpacker to INTERRUPTED if the previos state was INTERRUPT
- * or INTERRUPTED and returns whether interrupt was initiate or not.
- *
- * @return whether interrupt was initiate or not
- */
- private boolean performInterrupted()
- {
- synchronized (instances)
- {
- Object doIt = instances.get(this);
- if (doIt != null && (doIt.equals(INTERRUPT) || doIt.equals(INTERRUPTED)))
- {
- instances.put(this, INTERRUPTED);
- this.result = false;
- return (true);
- }
- return (false);
- }
- }
-
- /**
- * Returns whether interrupt was initiate or not for this Unpacker.
- *
- * @return whether interrupt was initiate or not
- */
- private boolean shouldInterrupt()
- {
- synchronized (instances)
- {
- Object doIt = instances.get(this);
- if (doIt != null && (doIt.equals(INTERRUPT) || doIt.equals(INTERRUPTED))) { return (true); }
- return (false);
- }
-
- }
-
protected File enterNextMediaMessage(String volumename, boolean lastcorrupt)
{
if (lastcorrupt)
@@ -837,555 +622,5 @@
Debug.trace("Error while writing config information in MultiVolumeUnpacker: "
+ e.getMessage());
}
- }
-
- /**
- * Return the state of the operation.
- *
- * @return true if the operation was successful, false otherwise.
- */
- public boolean getResult()
- {
- return this.result;
- }
-
- /**
- * @param updatechecks
- */
- private void performUpdateChecks(ArrayList updatechecks)
- {
- ArrayList include_patterns = new ArrayList();
- ArrayList exclude_patterns = new ArrayList();
-
- RECompiler recompiler = new RECompiler();
-
- this.absolute_installpath = new File(idata.getInstallPath()).getAbsoluteFile();
-
- // at first, collect all patterns
- for (Iterator iter = updatechecks.iterator(); iter.hasNext();)
- {
- UpdateCheck uc = (UpdateCheck) iter.next();
-
- if (uc.includesList != null)
- include_patterns.addAll(preparePatterns(uc.includesList, recompiler));
-
- if (uc.excludesList != null)
- exclude_patterns.addAll(preparePatterns(uc.excludesList, recompiler));
- }
-
- // do nothing if no update checks were specified
- if (include_patterns.size() == 0) return;
-
- // now collect all files in the installation directory and figure
- // out files to check for deletion
-
- // use a treeset for fast access
- TreeSet installed_files = new TreeSet();
-
- for (Iterator if_it = this.udata.getInstalledFilesList().iterator(); if_it.hasNext();)
- {
- String fname = (String) if_it.next();
-
- File f = new File(fname);
-
- if (!f.isAbsolute())
- {
- f = new File(this.absolute_installpath, fname);
- }
-
- installed_files.add(f.getAbsolutePath());
- }
-
- // now scan installation directory (breadth first), contains Files of
- // directories to scan
- // (note: we'll recurse infinitely if there are circular links or
- // similar nasty things)
- Stack scanstack = new Stack();
-
- // contains File objects determined for deletion
- ArrayList files_to_delete = new ArrayList();
-
- try
- {
- scanstack.add(absolute_installpath);
-
- while (!scanstack.empty())
- {
- File f = (File) scanstack.pop();
-
- File[] files = f.listFiles();
-
- if (files == null) { throw new IOException(f.getPath() + "is not a directory!"); }
-
- for (int i = 0; i < files.length; i++)
- {
- File newf = files[i];
-
- String newfname = newf.getPath();
-
- // skip files we just installed
- if (installed_files.contains(newfname)) continue;
-
- if (fileMatchesOnePattern(newfname, include_patterns)
- && (!fileMatchesOnePattern(newfname, exclude_patterns)))
- {
- files_to_delete.add(newf);
- }
-
- if (newf.isDirectory())
- {
- scanstack.push(newf);
- }
-
- }
- }
- }
- catch (IOException e)
- {
- this.handler.emitError("error while performing update checks", e.toString());
- }
-
- for (Iterator f_it = files_to_delete.iterator(); f_it.hasNext();)
- {
- File f = (File) f_it.next();
-
- if (!f.isDirectory())
- // skip directories - they cannot be removed safely yet
- {
-// this.handler.emitNotification("deleting " + f.getPath());
- f.delete();
- }
-
- }
-
- }
-
- /**
- * @param filename
- * @param patterns
- *
- * @return true if the file matched one pattern, false if it did not
- */
- private boolean fileMatchesOnePattern(String filename, ArrayList patterns)
- {
- // first check whether any include matches
- for (Iterator inc_it = patterns.iterator(); inc_it.hasNext();)
- {
- RE pattern = (RE) inc_it.next();
-
- if (pattern.match(filename)) { return true; }
- }
-
- return false;
- }
-
- /**
- * @param list A list of file name patterns (in ant fileset syntax)
- * @param recompiler The regular expression compiler (used to speed up RE compiling).
- *
- * @return List of org.apache.regexp.RE
- */
- private List preparePatterns(ArrayList list, RECompiler recompiler)
- {
- ArrayList result = new ArrayList();
-
- for (Iterator iter = list.iterator(); iter.hasNext();)
- {
- String element = (String) iter.next();
-
- if ((element != null) && (element.length() > 0))
- {
- // substitute variables in the pattern
- element = this.vs.substitute(element, "plain");
-
- // check whether the pattern is absolute or relative
- File f = new File(element);
-
- // if it is relative, make it absolute and prepend the
- // installation path
- // (this is a bit dangerous...)
- if (!f.isAbsolute())
- {
- element = new File(this.absolute_installpath, element).toString();
- }
-
- // now parse the element and construct a regular expression from
- // it
- // (we have to parse it one character after the next because
- // every
- // character should only be processed once - it's not possible
- // to get this
- // correct using regular expression replacing)
- StringBuffer element_re = new StringBuffer();
-
- int lookahead = -1;
-
- int pos = 0;
-
- while (pos < element.length())
- {
- char c;
-
- if (lookahead != -1)
- {
- c = (char) lookahead;
- lookahead = -1;
- }
- else
- c = element.charAt(pos++);
-
- switch (c)
- {
- case '/': {
- element_re.append(File.separator);
- break;
- }
- // escape backslash and dot
- case '\\':
- case '.': {
- element_re.append("\\");
- element_re.append(c);
- break;
- }
- case '*': {
- if (pos == element.length())
- {
- element_re.append("[^").append(File.separator).append("]*");
- break;
- }
-
- lookahead = element.charAt(pos++);
-
- // check for "**"
- if (lookahead == '*')
- {
- element_re.append(".*");
- // consume second star
- lookahead = -1;
- }
- else
- {
- element_re.append("[^").append(File.separator).append("]*");
- // lookahead stays there
- }
- break;
- }
- default: {
- element_re.append(c);
- break;
- }
- } // switch
-
- }
-
- // make sure that the whole expression is matched
- element_re.append('$');
-
- // replace \ by \\ and create a RE from the result
- try
- {
- result.add(new RE(recompiler.compile(element_re.toString())));
- }
- catch (RESyntaxException e)
- {
- this.handler.emitNotification("internal error: pattern \"" + element
- + "\" produced invalid RE \"" + f.getPath() + "\"");
- }
-
- }
- }
-
- return result;
- }
-
- /**
- * Puts the uninstaller.
- *
- * @exception Exception Description of the Exception
- */
- private void putUninstaller() throws Exception
- {
- // get the uninstaller base, returning if not found so that
- // idata.uninstallOutJar remains null
- InputStream[] in = new InputStream[2];
- in[0] = MultiVolumeUnpacker.class.getResourceAsStream("/res/IzPack.uninstaller");
- if (in[0] == null) return;
- // The uninstaller extension is facultative; it will be exist only
- // if a native library was marked for uninstallation.
- in[1] = MultiVolumeUnpacker.class.getResourceAsStream("/res/IzPack.uninstaller-ext");
-
- // Me make the .uninstaller directory
- String dest = IoHelper.translatePath("$INSTALL_PATH", vs) + File.separator + "Uninstaller";
- String jar = dest + File.separator + idata.info.getUninstallerName();
- File pathMaker = new File(dest);
- pathMaker.mkdirs();
-
- // We log the uninstaller deletion information
- udata.setUninstallerJarFilename(jar);
- udata.setUninstallerPath(dest);
-
- // We open our final jar file
- FileOutputStream out = new FileOutputStream(jar);
- // Intersect a buffer else byte for byte will be written to the file.
- BufferedOutputStream bos = new BufferedOutputStream(out);
- ZipOutputStream outJar = new ZipOutputStream(bos);
- idata.uninstallOutJar = outJar;
- outJar.setLevel(9);
- udata.addFile(jar, true);
-
- // We copy the uninstallers
- HashSet doubles = new HashSet();
-
- for (int i = 0; i < in.length; ++i)
- {
- if (in[i] == null) continue;
- ZipInputStream inRes = new ZipInputStream(in[i]);
- ZipEntry zentry = inRes.getNextEntry();
- while (zentry != null)
- {
- // Puts a new entry, but not twice like META-INF
- if (!doubles.contains(zentry.getName()))
- {
- doubles.add(zentry.getName());
- outJar.putNextEntry(new ZipEntry(zentry.getName()));
-
- // Byte to byte copy
- int unc = inRes.read();
- while (unc != -1)
- {
- outJar.write(unc);
- unc = inRes.read();
- }
-
- // Next one please
- inRes.closeEntry();
- outJar.closeEntry();
- }
- zentry = inRes.getNextEntry();
- }
- inRes.close();
- }
-
- // We put the langpack
- InputStream in2 = MultiVolumeUnpacker.class.getResourceAsStream("/langpacks/"
- + idata.localeISO3 + ".xml");
- outJar.putNextEntry(new ZipEntry("langpack.xml"));
- int read = in2.read();
- while (read != -1)
- {
- outJar.write(read);
- read = in2.read();
- }
- outJar.closeEntry();
- }
-
- // CUSTOM ACTION STUFF -------------- start -----------------
-
- /**
- * Informs all listeners which would be informed at the given action type.
- *
- * @param customActions array of lists with the custom action objects
- * @param action identifier for which callback should be called
- * @param firstParam first parameter for the call
- * @param secondParam second parameter for the call
- * @param thirdParam third parameter for the call
- */
- private void informListeners(List[] customActions, int action, Object firstParam,
- Object secondParam, Object thirdParam) throws Exception
- {
- List listener = null;
- // select the right action list.
- switch (action)
- {
- case InstallerListener.BEFORE_FILE:
- case InstallerListener.AFTER_FILE:
- case InstallerListener.BEFORE_DIR:
- case InstallerListener.AFTER_DIR:
- listener = customActions[customActions.length - 1];
- break;
- default:
- listener = customActions[0];
- break;
- }
- if (listener == null) return;
- // Iterate the action list.
- Iterator iter = listener.iterator();
- while (iter.hasNext())
- {
- if (shouldInterrupt()) return;
- InstallerListener il = (InstallerListener) iter.next();
- switch (action)
- {
- case InstallerListener.BEFORE_FILE:
- il.beforeFile((File) firstParam, (PackFile) secondParam);
- break;
- case InstallerListener.AFTER_FILE:
- il.afterFile((File) firstParam, (PackFile) secondParam);
- break;
- case InstallerListener.BEFORE_DIR:
- il.beforeDir((File) firstParam, (PackFile) secondParam);
- break;
- case InstallerListener.AFTER_DIR:
- il.afterDir((File) firstParam, (PackFile) secondParam);
- break;
- case InstallerListener.BEFORE_PACK:
- il.beforePack((Pack) firstParam, (Integer) secondParam,
- (AbstractUIProgressHandler) thirdParam);
- break;
- case InstallerListener.AFTER_PACK:
- il.afterPack((Pack) firstParam, (Integer) secondParam,
- (AbstractUIProgressHandler) thirdParam);
- break;
- case InstallerListener.BEFORE_PACKS:
- il.beforePacks((AutomatedInstallData) firstParam, (Integer) secondParam,
- (AbstractUIProgressHandler) thirdParam);
- break;
- case InstallerListener.AFTER_PACKS:
- il.afterPacks((AutomatedInstallData) firstParam,
- (AbstractUIProgressHandler) secondParam);
- break;
-
- }
- }
- }
-
- /**
- * Returns the defined custom actions split into types including a constructed type for the file
- * related installer listeners.
- *
- * @return array of lists of custom action data like listeners
- */
- private List[] getCustomActions()
- {
- String[] listenerNames = AutomatedInstallData.CUSTOM_ACTION_TYPES;
- List[] retval = new List[listenerNames.length + 1];
- int i;
- for (i = 0; i < listenerNames.length; ++i)
- {
- retval[i] = (List) idata.customData.get(listenerNames[i]);
- if (retval[i] == null)
- // Make a dummy list, then iterator is ever callable.
- retval[i] = new ArrayList();
- }
- if (retval[AutomatedInstallData.INSTALLER_LISTENER_INDEX].size() > 0)
- { // Installer listeners exist
- // Create file related installer listener list in the last
- // element of custom action array.
- i = retval.length - 1; // Should be so, but safe is safe ...
- retval[i] = new ArrayList();
- Iterator iter = ((List) retval[AutomatedInstallData.INSTALLER_LISTENER_INDEX])
- .iterator();
- while (iter.hasNext())
- {
- // If we get a class cast exception many is wrong and
- // we must fix it.
- InstallerListener li = (InstallerListener) iter.next();
- if (li.isFileListener()) retval[i].add(li);
- }
-
- }
- return (retval);
- }
-
- /**
- * Adds additional unistall data to the uninstall data object.
- *
- * @param udata unistall data
- * @param customData array of lists of custom action data like uninstaller listeners
- */
- private void handleAdditionalUninstallData(UninstallData udata, List[] customData)
- {
- // Handle uninstall libs
- udata.addAdditionalData("__uninstallLibs__",
- customData[AutomatedInstallData.UNINSTALLER_LIBS_INDEX]);
- // Handle uninstaller listeners
- udata.addAdditionalData("uninstallerListeners",
- customData[AutomatedInstallData.UNINSTALLER_LISTENER_INDEX]);
- // Handle uninstaller jars
- udata.addAdditionalData("uninstallerJars",
- customData[AutomatedInstallData.UNINSTALLER_JARS_INDEX]);
- }
-
- // This method is only used if a file related custom action exist.
- /**
- * Creates the given directory recursive and calls the method "afterDir" of each listener with
- * the current file object and the pack file object. On error an exception is raised.
- *
- * @param dest the directory which should be created
- * @param pf current pack file object
- * @param customActions all defined custom actions
- * @return false on error, true else
- * @throws Exception
- */
-
- private boolean mkDirsWithEnhancement(File dest, PackFile pf, List[] customActions)
- throws Exception
- {
- String path = "unknown";
- if (dest != null) path = dest.getAbsolutePath();
- if (dest != null && !dest.exists() && dest.getParentFile() != null)
- {
- if (dest.getParentFile().exists())
- informListeners(customActions, InstallerListener.BEFORE_DIR, dest, pf, null);
- if (!dest.mkdir())
- {
- mkDirsWithEnhancement(dest.getParentFile(), pf, customActions);
- if (!dest.mkdir()) dest = null;
- }
- informListeners(customActions, InstallerListener.AFTER_DIR, dest, pf, null);
- }
- if (dest == null)
- {
- handler.emitError("Error creating directories", "Could not create directory\n" + path);
- handler.stopAction();
- return (false);
- }
- return (true);
- }
-
- // CUSTOM ACTION STUFF -------------- end -----------------
-
- /**
- * Returns whether an interrupt request should be discarded or not.
- *
- * @return Returns the discard interrupt flag
- */
- public static synchronized boolean isDiscardInterrupt()
- {
- return discardInterrupt;
- }
-
- /**
- * Sets the discard interrupt flag.
- *
- * @param di the discard interrupt flag to set
- */
- public static synchronized void setDiscardInterrupt(boolean di)
- {
- discardInterrupt = di;
- setInterruptDesired(false);
- }
-
- /**
- * Returns the interrupt desired state.
- *
- * @return the interrupt desired state
- */
- public static boolean isInterruptDesired()
- {
- return interruptDesired;
- }
-
- /**
- * @param interruptDesired The interrupt desired flag to set
- */
- private static void setInterruptDesired(boolean interruptDesired)
- {
- MultiVolumeUnpacker.interruptDesired = interruptDesired;
- }
-
- public void setRules(RulesEngine rules)
- {
- this.rules = rules;
- }
+ }
}
\ No newline at end of file
Modified: izpack-src/trunk/src/lib/com/izforge/izpack/installer/Unpacker.java
===================================================================
--- izpack-src/trunk/src/lib/com/izforge/izpack/installer/Unpacker.java 2007-11-16 13:42:26 UTC (rev 1906)
+++ izpack-src/trunk/src/lib/com/izforge/izpack/installer/Unpacker.java 2007-11-16 14:20:55 UTC (rev 1907)
@@ -23,7 +23,6 @@
package com.izforge.izpack.installer;
import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -34,34 +33,19 @@
import java.lang.reflect.Constructor;
import java.net.URL;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
-import java.util.Stack;
-import java.util.TreeSet;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-import java.util.zip.ZipOutputStream;
-import org.apache.regexp.RE;
-import org.apache.regexp.RECompiler;
-import org.apache.regexp.RESyntaxException;
-
import com.izforge.izpack.ExecutableFile;
-import com.izforge.izpack.LocaleDatabase;
import com.izforge.izpack.Pack;
import com.izforge.izpack.PackFile;
import com.izforge.izpack.ParsableFile;
import com.izforge.izpack.UpdateCheck;
import com.izforge.izpack.event.InstallerListener;
-import com.izforge.izpack.rules.RulesEngine;
import com.izforge.izpack.util.AbstractUIHandler;
import com.izforge.izpack.util.AbstractUIProgressHandler;
import com.izforge.izpack.util.FileExecutor;
import com.izforge.izpack.util.IoHelper;
import com.izforge.izpack.util.OsConstraint;
-import com.izforge.izpack.util.VariableSubstitutor;
/**
* Unpacker class.
@@ -69,52 +53,11 @@
* @author Julien Ponge
* @author Johannes Lehtinen
*/
-public class Unpacker implements IUnpacker
-{
-
- /** The installdata. */
- private AutomatedInstallData idata;
-
- /** The installer listener. */
- private AbstractUIProgressHandler handler;
-
- /** The uninstallation data. */
- private UninstallData udata;
-
- /** The variables substitutor. */
- private VariableSubstitutor vs;
-
- /** The instances of the unpacker objects. */
- private static HashMap instances = new HashMap();
-
- /** The absolute path of the installation. (NOT the canonical!) */
- private File absolute_installpath;
-
- /** The packs locale database. */
- private LocaleDatabase langpack = null;
-
- /** Interrupt flag if global interrupt is desired. */
- private static boolean interruptDesired = false;
-
- /** Do not perform a interrupt call. */
- private static boolean discardInterrupt = false;
-
- /** The name of the XML file that specifies the panel langpack */
- private static final String LANG_FILE_NAME = "packsLang.xml";
-
- public static final String ALIVE = "alive";
-
- public static final String INTERRUPT = "doInterrupt";
-
- public static final String INTERRUPTED = "interruppted";
-
- /** The result of the operation. */
- private boolean result = true;
-
+public class Unpacker extends UnpackerBase
+{
private static final String tempPath = "$INSTALL_PATH/Uninstaller/IzpackWebTemp";
+
- private RulesEngine rules;
-
/**
* The constructor.
*
@@ -123,161 +66,9 @@
*/
public Unpacker(AutomatedInstallData idata, AbstractUIProgressHandler handler)
{
- //super("IzPack - Unpacker thread");
- try
- {
- String resource = LANG_FILE_NAME + "_" + idata.localeISO3;
- this.langpack = new LocaleDatabase(ResourceManager.getInstance().getInputStream(
- resource));
- }
- catch (Throwable exception)
- {}
+ super(idata,handler);
+ }
- this.idata = idata;
- this.handler = handler;
-
- // Initialize the variable substitutor
- vs = new VariableSubstitutor(idata.getVariables());
- }
-
- /**
- * Returns a copy of the active unpacker instances.
- *
- * @return a copy of active unpacker instances
- */
- public static HashMap getRunningInstances()
- {
- synchronized (instances)
- { // Return a shallow copy to prevent a
- // ConcurrentModificationException.
- return (HashMap) (instances.clone());
- }
- }
-
- /**
- * Adds this to the map of all existent instances of Unpacker.
- */
- private void addToInstances()
- {
- synchronized (instances)
- {
- instances.put(this, ALIVE);
- }
- }
-
- /**
- * Removes this from the map of all existent instances of Unpacker.
- */
- private void removeFromInstances()
- {
- synchronized (instances)
- {
- instances.remove(this);
- }
- }
-
- /**
- * Initiate interrupt of all alive Unpacker. This method does not interrupt the Unpacker objects
- * else it sets only the interrupt flag for the Unpacker objects. The dispatching of interrupt
- * will be performed by the Unpacker objects self.
- */
- private static void setInterruptAll()
- {
- synchronized (instances)
- {
- Iterator iter = instances.keySet().iterator();
- while (iter.hasNext())
- {
- Object key = iter.next();
- if (instances.get(key).equals(ALIVE))
- {
- instances.put(key, INTERRUPT);
- }
- }
- // Set global flag to allow detection of it in other classes.
- // Do not set it to thread because an exec will then be stoped.
- setInterruptDesired(true);
- }
- }
-
- /**
- * Initiate interrupt of all alive Unpacker and waits until all Unpacker are interrupted or the
- * wait time has arrived. If the doNotInterrupt flag in InstallerListener is set to true, the
- * interrupt will be discarded.
- *
- * @param waitTime wait time in millisecounds
- * @return true if the interrupt will be performed, false if the interrupt will be discarded
- */
- public static boolean interruptAll(long waitTime)
- {
- long t0 = System.currentTimeMillis();
- if (isDiscardInterrupt()) return (false);
- setInterruptAll();
- while (!isInterruptReady())
- {
- if (System.currentTimeMillis() - t0 > waitTime) return (true);
- try
- {
- Thread.sleep(100);
- }
- catch (InterruptedException e)
- {}
- }
- return (true);
- }
-
- private static boolean isInterruptReady()
- {
- synchronized (instances)
- {
- Iterator iter = instances.keySet().iterator();
- while (iter.hasNext())
- {
- Object key = iter.next();
- if (!instances.get(key).equals(INTERRUPTED)) return (false);
- }
- return (true);
- }
-
- }
-
- /**
- * Sets the interrupt flag for this Unpacker to INTERRUPTED if the previos state was INTERRUPT
- * or INTERRUPTED and returns whether interrupt was initiate or not.
- *
- * @return whether interrupt was initiate or not
- */
- private boolean performInterrupted()
- {
- synchronized (instances)
- {
- Object doIt = instances.get(this);
- if (doIt != null && (doIt.equals(INTERRUPT) || doIt.equals(INTERRUPTED)))
- {
- instances.put(this, INTERRUPTED);
- this.result = false;
- return (true);
- }
- return (false);
- }
- }
-
- /**
- * Returns whether interrupt was initiate or not for this Unpacker.
- *
- * @return whether interrupt was initiate or not
- */
- private boolean shouldInterrupt()
- {
- synchronized (instances)
- {
- Object doIt = instances.get(this);
- if (doIt != null && (doIt.equals(INTERRUPT) || doIt.equals(INTERRUPTED))) { return (true); }
- return (false);
- }
-
- }
-
/* (non-Javadoc)
* @see com.izforge.izpack.installer.IUnpacker#run()
*/
@@ -309,7 +100,7 @@
for (int i = 0; i < npacks; i++)
{
// We get the pack stream
- int n = idata.allPacks.indexOf(packs.get(i));
+ //int n = idata.allPacks.indexOf(packs.get(i));
Pack p = (Pack) packs.get(i);
// evaluate condition
@@ -685,345 +476,7 @@
}
}
- /* (non-Javadoc)
- * @see com.izforge.izpack.installer.IUnpacker#getResult()
- */
- public boolean getResult()
- {
- return this.result;
- }
-
/**
- * @param updatechecks
- */
- private void performUpdateChecks(ArrayList updatechecks)
- {
- ArrayList include_patterns = new ArrayList();
- ArrayList exclude_patterns = new ArrayList();
-
- RECompiler recompiler = new RECompiler();
-
- this.absolute_installpath = new File(idata.getInstallPath()).getAbsoluteFile();
-
- // at first, collect all patterns
- for (Iterator iter = updatechecks.iterator(); iter.hasNext();)
- {
- UpdateCheck uc = (UpdateCheck) iter.next();
-
- if (uc.includesList != null)
- include_patterns.addAll(preparePatterns(uc.includesList, recompiler));
-
- if (uc.excludesList != null)
- exclude_patterns.addAll(preparePatterns(uc.excludesList, recompiler));
- }
-
- // do nothing if no update checks were specified
- if (include_patterns.size() == 0) return;
-
- // now collect all files in the installation directory and figure
- // out files to check for deletion
-
- // use a treeset for fast access
- TreeSet installed_files = new TreeSet();
-
- for (Iterator if_it = this.udata.getInstalledFilesList().iterator(); if_it.hasNext();)
- {
- String fname = (String) if_it.next();
-
- File f = new File(fname);
-
- if (!f.isAbsolute())
- {
- f = new File(this.absolute_installpath, fname);
- }
-
- installed_files.add(f.getAbsolutePath());
- }
-
- // now scan installation directory (breadth first), contains Files of
- // directories to scan
- // (note: we'll recurse infinitely if there are circular links or
- // similar nasty things)
- Stack scanstack = new Stack();
-
- // contains File objects determined for deletion
- ArrayList files_to_delete = new ArrayList();
-
- try
- {
- scanstack.add(absolute_installpath);
-
- while (!scanstack.empty())
- {
- File f = (File) scanstack.pop();
-
- File[] files = f.listFiles();
-
- if (files == null) { throw new IOException(f.getPath() + "is not a directory!"); }
-
- for (int i = 0; i < files.length; i++)
- {
- File newf = files[i];
-
- String newfname = newf.getPath();
-
- // skip files we just installed
- if (installed_files.contains(newfname)) continue;
-
- if (fileMatchesOnePattern(newfname, include_patterns)
- && (!fileMatchesOnePattern(newfname, exclude_patterns)))
- {
- files_to_delete.add(newf);
- }
-
- if (newf.isDirectory())
- {
- scanstack.push(newf);
- }
-
- }
- }
- }
- catch (IOException e)
- {
- this.handler.emitError("error while performing update checks", e.toString());
- }
-
- for (Iterator f_it = files_to_delete.iterator(); f_it.hasNext();)
- {
- File f = (File) f_it.next();
-
- if (!f.isDirectory())
- // skip directories - they cannot be removed safely yet
- {
-// this.handler.emitNotification("deleting " + f.getPath());
- f.delete();
- }
-
- }
-
- }
-
- /**
- * @param filename
- * @param patterns
- *
- * @return true if the file matched one pattern, false if it did not
- */
- private boolean fileMatchesOnePattern(String filename, ArrayList patterns)
- {
- // first check whether any include matches
- for (Iterator inc_it = patterns.iterator(); inc_it.hasNext();)
- {
- RE pattern = (RE) inc_it.next();
-
- if (pattern.match(filename)) { return true; }
- }
-
- return false;
- }
-
- /**
- * @param list A list of file name patterns (in ant fileset syntax)
- * @param recompiler The regular expression compiler (used to speed up RE compiling).
- *
- * @return List of org.apache.regexp.RE
- */
- private List preparePatterns(ArrayList list, RECompiler recompiler)
- {
- ArrayList result = new ArrayList();
-
- for (Iterator iter = list.iterator(); iter.hasNext();)
- {
- String element = (String) iter.next();
-
- if ((element != null) && (element.length() > 0))
- {
- // substitute variables in the pattern
- element = this.vs.substitute(element, "plain");
-
- // check whether the pattern is absolute or relative
- File f = new File(element);
-
- // if it is relative, make it absolute and prepend the
- // installation path
- // (this is a bit dangerous...)
- if (!f.isAbsolute())
- {
- element = new File(this.absolute_installpath, element).toString();
- }
-
- // now parse the element and construct a regular expression from
- // it
- // (we have to parse it one character after the next because
- // every
- // character should only be processed once - it's not possible
- // to get this
- // correct using regular expression replacing)
- StringBuffer element_re = new StringBuffer();
-
- int lookahead = -1;
-
- int pos = 0;
-
- while (pos < element.length())
- {
- char c;
-
- if (lookahead != -1)
- {
- c = (char) lookahead;
- lookahead = -1;
- }
- else
- c = element.charAt(pos++);
-
- switch (c)
- {
- case '/': {
- element_re.append(File.separator);
- break;
- }
- // escape backslash and dot
- case '\\':
- case '.': {
- element_re.append("\\");
- element_re.append(c);
- break;
- }
- case '*': {
- if (pos == element.length())
- {
- element_re.append("[^").append(File.separator).append("]*");
- break;
- }
-
- lookahead = element.charAt(pos++);
-
- // check for "**"
- if (lookahead == '*')
- {
- element_re.append(".*");
- // consume second star
- lookahead = -1;
- }
- else
- {
- element_re.append("[^").append(File.separator).append("]*");
- // lookahead stays there
- }
- break;
- }
- default: {
- element_re.append(c);
- break;
- }
- } // switch
-
- }
-
- // make sure that the whole expression is matched
- element_re.append('$');
-
- // replace \ by \\ and create a RE from the result
- try
- {
- result.add(new RE(recompiler.compile(element_re.toString())));
- }
- catch (RESyntaxException e)
- {
- this.handler.emitNotification("internal error: pattern \"" + element
- + "\" produced invalid RE \"" + f.getPath() + "\"");
- }
-
- }
- }
-
- return result;
- }
-
- /**
- * Puts the uninstaller.
- *
- * @exception Exception Description of the Exception
- */
- private void putUninstaller() throws Exception
- {
- // get the uninstaller base, returning if not found so that
- // idata.uninstallOutJar remains null
- InputStream[] in = new InputStream[2];
- in[0] = Unpacker.class.getResourceAsStream("/res/IzPack.uninstaller");
- if (in[0] == null) return;
- // The uninstaller extension is facultative; it will be exist only
- // if a native library was marked for uninstallation.
- in[1] = Unpacker.class.getResourceAsStream("/res/IzPack.uninstaller-ext");
-
- // Me make the .uninstaller directory
- String dest = IoHelper.translatePath("$INSTALL_PATH", vs) + File.separator + "Uninstaller";
- String jar = dest + File.separator + idata.info.getUninstallerName();
- File pathMaker = new File(dest);
- pathMaker.mkdirs();
-
- // We log the uninstaller deletion information
- udata.setUninstallerJarFilename(jar);
- udata.setUninstallerPath(dest);
-
- // We open our final jar file
- FileOutputStream out = new FileOutputStream(jar);
- // Intersect a buffer else byte for byte will be written to the file.
- BufferedOutputStream bos = new BufferedOutputStream(out);
- ZipOutputStream outJar = new ZipOutputStream(bos);
- idata.uninstallOutJar = outJar;
- outJar.setLevel(9);
- udata.addFile(jar, true);
-
- // We copy the uninstallers
- HashSet doubles = new HashSet();
-
- for (int i = 0; i < in.length; ++i)
- {
- if (in[i] == null) continue;
- ZipInputStream inRes = new ZipInputStream(in[i]);
- ZipEntry zentry = inRes.getNextEntry();
- while (zentry != null)
- {
- // Puts a new entry, but not twice like META-INF
- if (!doubles.contains(zentry.getName()))
- {
- doubles.add(zentry.getName());
- outJar.putNextEntry(new ZipEntry(zentry.getName()));
-
- // Byte to byte copy
- int unc = inRes.read();
- while (unc != -1)
- {
- outJar.write(unc);
- unc = inRes.read();
- }
-
- // Next one please
- inRes.closeEntry();
- outJar.closeEntry();
- }
- zentry = inRes.getNextEntry();
- }
- inRes.close();
- }
-
- // We put the langpack
- InputStream in2 = Unpacker.class.getResourceAsStream("/langpacks/" + idata.localeISO3
- + ".xml");
- outJar.putNextEntry(new ZipEntry("langpack.xml"));
- int read = in2.read();
- while (read != -1)
- {
- outJar.write(read);
- read = in2.read();
- }
- outJar.closeEntry();
- }
-
- /**
* Returns a stream to a pack, location depending on if it's web based.
*
* @param n The pack number.
@@ -1099,215 +552,5 @@
}
return in;
- }
-
- // CUSTOM ACTION STUFF -------------- start -----------------
-
- /**
- * Informs all listeners which would be informed at the given action type.
- *
- * @param customActions array of lists with the custom action objects
- * @param action identifier for which callback should be called
- * @param firstParam first parameter for the call
- * @param secondParam second parameter for the call
- * @param thirdParam third parameter for the call
- */
- private void informListeners(List[] customActions, int action, Object firstParam,
- Object secondParam, Object thirdParam) throws Exception
- {
- List listener = null;
- // select the right action list.
- switch (action)
- {
- case InstallerListener.BEFORE_FILE:
- case InstallerListener.AFTER_FILE:
- case InstallerListener.BEFORE_DIR:
- case InstallerListener.AFTER_DIR:
- listener = customActions[customActions.length - 1];
- break;
- default:
- listener = customActions[0];
- break;
- }
- if (listener == null) return;
- // Iterate the action list.
- Iterator iter = listener.iterator();
- while (iter.hasNext())
- {
- if (shouldInterrupt()) return;
- InstallerListener il = (InstallerListener) iter.next();
- switch (action)
- {
- case InstallerListener.BEFORE_FILE:
- il.beforeFile((File) firstParam, (PackFile) secondParam);
- break;
- case InstallerListener.AFTER_FILE:
- il.afterFile((File) firstParam, (PackFile) secondParam);
- break;
- case InstallerListener.BEFORE_DIR:
- il.beforeDir((File) firstParam, (PackFile) secondParam);
- break;
- case InstallerListener.AFTER_DIR:
- il.afterDir((File) firstParam, (PackFile) secondParam);
- break;
- case InstallerListener.BEFORE_PACK:
- il.beforePack((Pack) firstParam, (Integer) secondParam,
- (AbstractUIProgressHandler) thirdParam);
- break;
- case InstallerListener.AFTER_PACK:
- il.afterPack((Pack) firstParam, (Integer) secondParam,
- (AbstractUIProgressHandler) thirdParam);
- break;
- case InstallerListener.BEFORE_PACKS:
- il.beforePacks((AutomatedInstallData) firstParam, (Integer) secondParam,
- (AbstractUIProgressHandler) thirdParam);
- break;
- case InstallerListener.AFTER_PACKS:
- il.afterPacks((AutomatedInstallData) firstParam,
- (AbstractUIProgressHandler) secondParam);
- break;
-
- }
- }
- }
-
- /**
- * Returns the defined custom actions split into types including a constructed type for the file
- * related installer listeners.
- *
- * @return array of lists of custom action data like listeners
- */
- private List[] getCustomActions()
- {
- String[] listenerNames = AutomatedInstallData.CUSTOM_ACTION_TYPES;
- List[] retval = new List[listenerNames.length + 1];
- int i;
- for (i = 0; i < listenerNames.length; ++i)
- {
- retval[i] = (List) idata.customData.get(listenerNames[i]);
- if (retval[i] == null)
- // Make a dummy list, then iterator is ever callable.
- retval[i] = new ArrayList();
- }
- if (retval[AutomatedInstallData.INSTALLER_LISTENER_INDEX].size() > 0)
- { // Installer listeners exist
- // Create file related installer listener list in the last
- // element of custom action array.
- i = retval.length - 1; // Should be so, but safe is safe ...
- retval[i] = new ArrayList();
- Iterator iter = ((List) retval[AutomatedInstallData.INSTALLER_LISTENER_INDEX])
- .iterator();
- while (iter.hasNext())
- {
- // If we get a class cast exception many is wrong and
- // we must fix it.
- InstallerListener li = (InstallerListener) iter.next();
- if (li.isFileListener()) retval[i].add(li);
- }
-
- }
- return (retval);
- }
-
- /**
- * Adds additional unistall data to the uninstall data object.
- *
- * @param udata unistall data
- * @param customData array of lists of custom action data like uninstaller listeners
- */
- private void handleAdditionalUninstallData(UninstallData udata, List[] customData)
- {
- // Handle uninstall libs
- udata.addAdditionalData("__uninstallLibs__",
- customData[AutomatedInstallData.UNINSTALLER_LIBS_INDEX]);
- // Handle uninstaller listeners
- udata.addAdditionalData("uninstallerListeners",
- customData[AutomatedInstallData.UNINSTALLER_LISTENER_INDEX]);
- // Handle uninstaller jars
- udata.addAdditionalData("uninstallerJars",
- customData[AutomatedInstallData.UNINSTALLER_JARS_INDEX]);
- }
-
- // This method is only used if a file related custom action exist.
- /**
- * Creates the given directory recursive and calls the method "afterDir" of each listener with
- * the current file object and the pack file object. On error an exception is raised.
- *
- * @param dest the directory which should be created
- * @param pf current pack file object
- * @param customActions all defined custom actions
- * @return false on error, true else
- * @throws Exception
- */
-
- private boolean mkDirsWithEnhancement(File dest, PackFile pf, List[] customActions)
- throws Exception
- {
- String path = "unknown";
- if (dest != null) path = dest.getAbsolutePath();
- if (dest != null && !dest.exists() && dest.getParentFile() != null)
- {
- if (dest.getParentFile().exists())
- informListeners(customActions, InstallerListener.BEFORE_DIR, dest, pf, null);
- if (!dest.mkdir())
- {
- mkDirsWithEnhancement(dest.getParentFile(), pf, customActions);
- if (!dest.mkdir()) dest = null;
- }
- informListeners(customActions, InstallerListener.AFTER_DIR, dest, pf, null);
- }
- if (dest == null)
- {
- handler.emitError("Error creating directories", "Could not create directory\n" + path);
- handler.stopAction();
- return (false);
- }
- return (true);
- }
-
- // CUSTOM ACTION STUFF -------------- end -----------------
-
- /**
- * Returns whether an interrupt request should be discarded or not.
- *
- * @return Returns the discard interrupt flag
- */
- public static synchronized boolean isDiscardInterrupt()
- {
- return discardInterrupt;
- }
-
- /**
- * Sets the discard interrupt flag.
- *
- * @param di the discard interrupt flag to set
- */
- public static synchronized void setDiscardInterrupt(boolean di)
- {
- discardInterrupt = di;
- setInterruptDesired(false);
- }
-
- /**
- * Returns the interrupt desired state.
- *
- * @return the interrupt desired state
- */
- public static boolean isInterruptDesired()
- {
- return interruptDesired;
- }
-
- /**
- * @param interruptDesired The interrupt desired flag to set
- */
- private static void setInterruptDesired(boolean interruptDesired)
- {
- Unpacker.interruptDesired = interruptDesired;
- }
-
- public void setRules(RulesEngine rules)
- {
- this.rules = rules;
- }
+ }
}
Added: izpack-src/trunk/src/lib/com/izforge/izpack/installer/UnpackerBase.java
===================================================================
--- izpack-src/trunk/src/lib/com/izforge/izpack/installer/UnpackerBase.java 2007-11-16 13:42:26 UTC (rev 1906)
+++ izpack-src/trunk/src/lib/com/izforge/izpack/installer/UnpackerBase.java 2007-11-16 14:20:55 UTC (rev 1907)
@@ -0,0 +1,809 @@
+/*
+ * IzPack - Copyright 2001-2007 Julien Ponge, All Rights Reserved.
+ *
+ * https://izpack.github.io/
+ * http://developer.berlios.de/projects/izpack/
+ *
+ * Copyright 2007 Dennis Reil
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.izforge.izpack.installer;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+import java.util.TreeSet;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.regexp.RE;
+import org.apache.regexp.RECompiler;
+import org.apache.regexp.RESyntaxException;
+
+import com.izforge.izpack.LocaleDatabase;
+import com.izforge.izpack.Pack;
+import com.izforge.izpack.PackFile;
+import com.izforge.izpack.UpdateCheck;
+import com.izforge.izpack.event.InstallerListener;
+import com.izforge.izpack.rules.RulesEngine;
+import com.izforge.izpack.util.AbstractUIProgressHandler;
+import com.izforge.izpack.util.IoHelper;
+import com.izforge.izpack.util.VariableSubstitutor;
+
+/**
+ * Abstract base class for all unpacker implementations.
+ * @author Dennis Reil, <izpack at reil-online.de>
+ */
+public abstract class UnpackerBase implements IUnpacker
+{
+ /** The installdata. */
+ protected AutomatedInstallData idata;
+
+ /** The installer listener. */
+ protected AbstractUIProgressHandler handler;
+
+ /** The uninstallation data. */
+ protected UninstallData udata;
+
+ /** The variables substitutor. */
+ protected VariableSubstitutor vs;
+
+ /** The absolute path of the installation. (NOT the canonical!) */
+ protected File absolute_installpath;
+
+ /** The packs locale database. */
+ protected LocaleDatabase langpack = null;
+
+ /** The result of the operation. */
+ protected boolean result = true;
+
+ /** The instances of the unpacker objects. */
+ protected static HashMap instances = new HashMap();
+
+ /** Interrupt flag if global interrupt is desired. */
+ protected static boolean interruptDesired = false;
+
+ /** Do not perform a interrupt call. */
+ protected static boolean discardInterrupt = false;
+
+ /** The name of the XML file that specifies the panel langpack */
+ protected static final String LANG_FILE_NAME = "packsLang.xml";
+
+ public static final String ALIVE = "alive";
+
+ public static final String INTERRUPT = "doInterrupt";
+
+ public static final String INTERRUPTED = "interruppted";
+
+ protected RulesEngine rules;
+
+ /**
+ * The constructor.
+ *
+ * @param idata The installation data.
+ * @param handler The installation progress handler.
+ */
+ public UnpackerBase(AutomatedInstallData idata, AbstractUIProgressHandler handler)
+ {
+ try
+ {
+ String resource = LANG_FILE_NAME + "_" + idata.localeISO3;
+ this.langpack = new LocaleDatabase(ResourceManager.getInstance().getInputStream(resource));
+ }
+ catch (Throwable exception)
+ {}
+
+ this.idata = idata;
+ this.handler = handler;
+
+ // Initialize the variable substitutor
+ vs = new VariableSubstitutor(idata.getVariables());
+ }
+
+ public void setRules(RulesEngine rules)
+ {
+ this.rules = rules;
+ }
+
+ /**
+ * Returns a copy of the active unpacker instances.
+ *
+ * @return a copy of active unpacker instances
+ */
+ public static HashMap getRunningInstances()
+ {
+ synchronized (instances)
+ { // Return a shallow copy to prevent a
+ // ConcurrentModificationException.
+ return (HashMap) (instances.clone());
+ }
+ }
+
+ /**
+ * Adds this to the map of all existent instances of Unpacker.
+ */
+ protected void addToInstances()
+ {
+ synchronized (instances)
+ {
+ instances.put(this, ALIVE);
+ }
+ }
+
+ /**
+ * Removes this from the map of all existent instances of Unpacker.
+ */
+ protected void removeFromInstances()
+ {
+ synchronized (instances)
+ {
+ instances.remove(this);
+ }
+ }
+
+ /**
+ * Initiate interrupt of all alive Unpacker. This method does not interrupt the Unpacker objects
+ * else it sets only the interrupt flag for the Unpacker objects. The dispatching of interrupt
+ * will be performed by the Unpacker objects self.
+ */
+ private static void setInterruptAll()
+ {
+ synchronized (instances)
+ {
+ Iterator iter = instances.keySet().iterator();
+ while (iter.hasNext())
+ {
+ Object key = iter.next();
+ if (instances.get(key).equals(ALIVE))
+ {
+ instances.put(key, INTERRUPT);
+ }
+ }
+ // Set global flag to allow detection of it in other classes.
+ // Do not set it to thread because an exec will then be stoped.
+ setInterruptDesired(true);
+ }
+ }
+
+ /**
+ * Initiate interrupt of all alive Unpacker and waits until all Unpacker are interrupted or the
+ * wait time has arrived. If the doNotInterrupt flag in InstallerListener is set to true, the
+ * interrupt will be discarded.
+ *
+ * @param waitTime wait time in millisecounds
+ * @return true if the interrupt will be performed, false if the interrupt will be discarded
+ */
+ public static boolean interruptAll(long waitTime)
+ {
+ long t0 = System.currentTimeMillis();
+ if (isDiscardInterrupt()) return (false);
+ setInterruptAll();
+ while (!isInterruptReady())
+ {
+ if (System.currentTimeMillis() - t0 > waitTime) return (true);
+ try
+ {
+ Thread.sleep(100);
+ }
+ catch (InterruptedException e)
+ {}
+ }
+ return (true);
+ }
+
+ private static boolean isInterruptReady()
+ {
+ synchronized (instances)
+ {
+ Iterator iter = instances.keySet().iterator();
+ while (iter.hasNext())
+ {
+ Object key = iter.next();
+ if (!instances.get(key).equals(INTERRUPTED)) return (false);
+ }
+ return (true);
+ }
+
+ }
+
+ /**
+ * Sets the interrupt flag for this Unpacker to INTERRUPTED if the previos state was INTERRUPT
+ * or INTERRUPTED and returns whether interrupt was initiate or not.
+ *
+ * @return whether interrupt was initiate or not
+ */
+ protected boolean performInterrupted()
+ {
+ synchronized (instances)
+ {
+ Object doIt = instances.get(this);
+ if (doIt != null && (doIt.equals(INTERRUPT) || doIt.equals(INTERRUPTED)))
+ {
+ instances.put(this, INTERRUPTED);
+ this.result = false;
+ return (true);
+ }
+ return (false);
+ }
+ }
+
+ /**
+ * Returns whether interrupt was initiate or not for this Unpacker.
+ *
+ * @return whether interrupt was initiate or not
+ */
+ private boolean shouldInterrupt()
+ {
+ synchronized (instances)
+ {
+ Object doIt = instances.get(this);
+ if (doIt != null && (doIt.equals(INTERRUPT) || doIt.equals(INTERRUPTED))) { return (true); }
+ return (false);
+ }
+
+ }
+
+ /**
+ * Return the state of the operation.
+ *
+ * @return true if the operation was successful, false otherwise.
+ */
+ public boolean getResult()
+ {
+ return this.result;
+ }
+
+ /**
+ * @param filename
+ * @param patterns
+ *
+ * @return true if the file matched one pattern, false if it did not
+ */
+ private boolean fileMatchesOnePattern(String filename, ArrayList patterns)
+ {
+ // first check whether any include matches
+ for (Iterator inc_it = patterns.iterator(); inc_it.hasNext();)
+ {
+ RE pattern = (RE) inc_it.next();
+
+ if (pattern.match(filename)) { return true; }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param list A list of file name patterns (in ant fileset syntax)
+ * @param recompiler The regular expression compiler (used to speed up RE compiling).
+ *
+ * @return List of org.apache.regexp.RE
+ */
+ private List preparePatterns(ArrayList list, RECompiler recompiler)
+ {
+ ArrayList result = new ArrayList();
+
+ for (Iterator iter = list.iterator(); iter.hasNext();)
+ {
+ String element = (String) iter.next();
+
+ if ((element != null) && (element.length() > 0))
+ {
+ // substitute variables in the pattern
+ element = this.vs.substitute(element, "plain");
+
+ // check whether the pattern is absolute or relative
+ File f = new File(element);
+
+ // if it is relative, make it absolute and prepend the
+ // installation path
+ // (this is a bit dangerous...)
+ if (!f.isAbsolute())
+ {
+ element = new File(this.absolute_installpath, element).toString();
+ }
+
+ // now parse the element and construct a regular expression from
+ // it
+ // (we have to parse it one character after the next because
+ // every
+ // character should only be processed once - it's not possible
+ // to get this
+ // correct using regular expression replacing)
+ StringBuffer element_re = new StringBuffer();
+
+ int lookahead = -1;
+
+ int pos = 0;
+
+ while (pos < element.length())
+ {
+ char c;
+
+ if (lookahead != -1)
+ {
+ c = (char) lookahead;
+ lookahead = -1;
+ }
+ else
+ c = element.charAt(pos++);
+
+ switch (c)
+ {
+ case '/': {
+ element_re.append(File.separator);
+ break;
+ }
+ // escape backslash and dot
+ case '\\':
+ case '.': {
+ element_re.append("\\");
+ element_re.append(c);
+ break;
+ }
+ case '*': {
+ if (pos == element.length())
+ {
+ element_re.append("[^").append(File.separator).append("]*");
+ break;
+ }
+
+ lookahead = element.charAt(pos++);
+
+ // check for "**"
+ if (lookahead == '*')
+ {
+ element_re.append(".*");
+ // consume second star
+ lookahead = -1;
+ }
+ else
+ {
+ element_re.append("[^").append(File.separator).append("]*");
+ // lookahead stays there
+ }
+ break;
+ }
+ default: {
+ element_re.append(c);
+ break;
+ }
+ } // switch
+
+ }
+
+ // make sure that the whole expression is matched
+ element_re.append('$');
+
+ // replace \ by \\ and create a RE from the result
+ try
+ {
+ result.add(new RE(recompiler.compile(element_re.toString())));
+ }
+ catch (RESyntaxException e)
+ {
+ this.handler.emitNotification("internal error: pattern \"" + element
+ + "\" produced invalid RE \"" + f.getPath() + "\"");
+ }
+
+ }
+ }
+
+ return result;
+ }
+
+ // CUSTOM ACTION STUFF -------------- start -----------------
+
+ /**
+ * Informs all listeners which would be informed at the given action type.
+ *
+ * @param customActions array of lists with the custom action objects
+ * @param action identifier for which callback should be called
+ * @param firstParam first parameter for the call
+ * @param secondParam second parameter for the call
+ * @param thirdParam third parameter for the call
+ */
+ protected void informListeners(List[] customActions, int action, Object firstParam,
+ Object secondParam, Object thirdParam) throws Exception
+ {
+ List listener = null;
+ // select the right action list.
+ switch (action)
+ {
+ case InstallerListener.BEFORE_FILE:
+ case InstallerListener.AFTER_FILE:
+ case InstallerListener.BEFORE_DIR:
+ case InstallerListener.AFTER_DIR:
+ listener = customActions[customActions.length - 1];
+ break;
+ default:
+ listener = customActions[0];
+ break;
+ }
+ if (listener == null) return;
+ // Iterate the action list.
+ Iterator iter = listener.iterator();
+ while (iter.hasNext())
+ {
+ if (shouldInterrupt()) return;
+ InstallerListener il = (InstallerListener) iter.next();
+ switch (action)
+ {
+ case InstallerListener.BEFORE_FILE:
+ il.beforeFile((File) firstParam, (PackFile) secondParam);
+ break;
+ case InstallerListener.AFTER_FILE:
+ il.afterFile((File) firstParam, (PackFile) secondParam);
+ break;
+ case InstallerListener.BEFORE_DIR:
+ il.beforeDir((File) firstParam, (PackFile) secondParam);
+ break;
+ case InstallerListener.AFTER_DIR:
+ il.afterDir((File) firstParam, (PackFile) secondParam);
+ break;
+ case InstallerListener.BEFORE_PACK:
+ il.beforePack((Pack) firstParam, (Integer) secondParam,
+ (AbstractUIProgressHandler) thirdParam);
+ break;
+ case InstallerListener.AFTER_PACK:
+ il.afterPack((Pack) firstParam, (Integer) secondParam,
+ (AbstractUIProgressHandler) thirdParam);
+ break;
+ case InstallerListener.BEFORE_PACKS:
+ il.beforePacks((AutomatedInstallData) firstParam, (Integer) secondParam,
+ (AbstractUIProgressHandler) thirdParam);
+ break;
+ case InstallerListener.AFTER_PACKS:
+ il.afterPacks((AutomatedInstallData) firstParam,
+ (AbstractUIProgressHandler) secondParam);
+ break;
+
+ }
+ }
+ }
+
+ /**
+ * Returns the defined custom actions split into types including a constructed type for the file
+ * related installer listeners.
+ *
+ * @return array of lists of custom action data like listeners
+ */
+ protected List[] getCustomActions()
+ {
+ String[] listenerNames = AutomatedInstallData.CUSTOM_ACTION_TYPES;
+ List[] retval = new List[listenerNames.length + 1];
+ int i;
+ for (i = 0; i < listenerNames.length; ++i)
+ {
+ retval[i] = (List) idata.customData.get(listenerNames[i]);
+ if (retval[i] == null)
+ // Make a dummy list, then iterator is ever callable.
+ retval[i] = new ArrayList();
+ }
+ if (retval[AutomatedInstallData.INSTALLER_LISTENER_INDEX].size() > 0)
+ { // Installer listeners exist
+ // Create file related installer listener list in the last
+ // element of custom action array.
+ i = retval.length - 1; // Should be so, but safe is safe ...
+ retval[i] = new ArrayList();
+ Iterator iter = ((List) retval[AutomatedInstallData.INSTALLER_LISTENER_INDEX])
+ .iterator();
+ while (iter.hasNext())
+ {
+ // If we get a class cast exception many is wrong and
+ // we must fix it.
+ InstallerListener li = (InstallerListener) iter.next();
+ if (li.isFileListener()) retval[i].add(li);
+ }
+
+ }
+ return (retval);
+ }
+
+ // This method is only used if a file related custom action exist.
+ /**
+ * Creates the given directory recursive and calls the method "afterDir" of each listener with
+ * the current file object and the pack file object. On error an exception is raised.
+ *
+ * @param dest the directory which should be created
+ * @param pf current pack file object
+ * @param customActions all defined custom actions
+ * @return false on error, true else
+ * @throws Exception
+ */
+
+ protected boolean mkDirsWithEnhancement(File dest, PackFile pf, List[] customActions)
+ throws Exception
+ {
+ String path = "unknown";
+ if (dest != null) path = dest.getAbsolutePath();
+ if (dest != null && !dest.exists() && dest.getParentFile() != null)
+ {
+ if (dest.getParentFile().exists())
+ informListeners(customActions, InstallerListener.BEFORE_DIR, dest, pf, null);
+ if (!dest.mkdir())
+ {
+ mkDirsWithEnhancement(dest.getParentFile(), pf, customActions);
+ if (!dest.mkdir()) dest = null;
+ }
+ informListeners(customActions, InstallerListener.AFTER_DIR, dest, pf, null);
+ }
+ if (dest == null)
+ {
+ handler.emitError("Error creating directories", "Could not create directory\n" + path);
+ handler.stopAction();
+ return (false);
+ }
+ return (true);
+ }
+
+ // CUSTOM ACTION STUFF -------------- end -----------------
+
+ /**
+ * Returns whether an interrupt request should be discarded or not.
+ *
+ * @return Returns the discard interrupt flag
+ */
+ public static synchronized boolean isDiscardInterrupt()
+ {
+ return discardInterrupt;
+ }
+
+ /**
+ * Sets the discard interrupt flag.
+ *
+ * @param di the discard interrupt flag to set
+ */
+ public static synchronized void setDiscardInterrupt(boolean di)
+ {
+ discardInterrupt = di;
+ setInterruptDesired(false);
+ }
+
+ /**
+ * Returns the interrupt desired state.
+ *
+ * @return the interrupt desired state
+ */
+ public static boolean isInterruptDesired()
+ {
+ return interruptDesired;
+ }
+
+ /**
+ * @param interruptDesired The interrupt desired flag to set
+ */
+ private static void setInterruptDesired(boolean interruptDesired)
+ {
+ UnpackerBase.interruptDesired = interruptDesired;
+ }
+
+ /**
+ * Puts the uninstaller.
+ *
+ * @exception Exception Description of the Exception
+ */
+ protected void putUninstaller() throws Exception
+ {
+ // get the uninstaller base, returning if not found so that
+ // idata.uninstallOutJar remains null
+ InputStream[] in = new InputStream[2];
+ in[0] = UnpackerBase.class.getResourceAsStream("/res/IzPack.uninstaller");
+ if (in[0] == null) return;
+ // The uninstaller extension is facultative; it will be exist only
+ // if a native library was marked for uninstallation.
+ in[1] = UnpackerBase.class.getResourceAsStream("/res/IzPack.uninstaller-ext");
+
+ // Me make the .uninstaller directory
+ String dest = IoHelper.translatePath("$INSTALL_PATH", vs) + File.separator + "Uninstaller";
+ String jar = dest + File.separator + idata.info.getUninstallerName();
+ File pathMaker = new File(dest);
+ pathMaker.mkdirs();
+
+ // We log the uninstaller deletion information
+ udata.setUninstallerJarFilename(jar);
+ udata.setUninstallerPath(dest);
+
+ // We open our final jar file
+ FileOutputStream out = new FileOutputStream(jar);
+ // Intersect a buffer else byte for byte will be written to the file.
+ BufferedOutputStream bos = new BufferedOutputStream(out);
+ ZipOutputStream outJar = new ZipOutputStream(bos);
+ idata.uninstallOutJar = outJar;
+ outJar.setLevel(9);
+ udata.addFile(jar, true);
+
+ // We copy the uninstallers
+ HashSet doubles = new HashSet();
+
+ for (int i = 0; i < in.length; ++i)
+ {
+ if (in[i] == null) continue;
+ ZipInputStream inRes = new ZipInputStream(in[i]);
+ ZipEntry zentry = inRes.getNextEntry();
+ while (zentry != null)
+ {
+ // Puts a new entry, but not twice like META-INF
+ if (!doubles.contains(zentry.getName()))
+ {
+ doubles.add(zentry.getName());
+ outJar.putNextEntry(new ZipEntry(zentry.getName()));
+
+ // Byte to byte copy
+ int unc = inRes.read();
+ while (unc != -1)
+ {
+ outJar.write(unc);
+ unc = inRes.read();
+ }
+
+ // Next one please
+ inRes.closeEntry();
+ outJar.closeEntry();
+ }
+ zentry = inRes.getNextEntry();
+ }
+ inRes.close();
+ }
+
+ // We put the langpack
+ InputStream in2 = Unpacker.class.getResourceAsStream("/langpacks/" + idata.localeISO3 + ".xml");
+ outJar.putNextEntry(new ZipEntry("langpack.xml"));
+ int read = in2.read();
+ while (read != -1)
+ {
+ outJar.write(read);
+ read = in2.read();
+ }
+ outJar.closeEntry();
+ }
+
+ /**
+ * Adds additional unistall data to the uninstall data object.
+ *
+ * @param udata unistall data
+ * @param customData array of lists of custom action data like uninstaller listeners
+ */
+ protected void handleAdditionalUninstallData(UninstallData udata, List[] customData)
+ {
+ // Handle uninstall libs
+ udata.addAdditionalData("__uninstallLibs__", customData[AutomatedInstallData.UNINSTALLER_LIBS_INDEX]);
+ // Handle uninstaller listeners
+ udata.addAdditionalData("uninstallerListeners", customData[AutomatedInstallData.UNINSTALLER_LISTENER_INDEX]);
+ // Handle uninstaller jars
+ udata.addAdditionalData("uninstallerJars", customData[AutomatedInstallData.UNINSTALLER_JARS_INDEX]);
+ }
+
+ public abstract void run();
+
+ /**
+ * @param updatechecks
+ */
+ protected void performUpdateChecks(ArrayList updatechecks)
+ {
+ ArrayList include_patterns = new ArrayList();
+ ArrayList exclude_patterns = new ArrayList();
+
+ RECompiler recompiler = new RECompiler();
+
+ this.absolute_installpath = new File(idata.getInstallPath()).getAbsoluteFile();
+
+ // at first, collect all patterns
+ for (Iterator iter = updatechecks.iterator(); iter.hasNext();)
+ {
+ UpdateCheck uc = (UpdateCheck) iter.next();
+
+ if (uc.includesList != null)
+ include_patterns.addAll(preparePatterns(uc.includesList, recompiler));
+
+ if (uc.excludesList != null)
+ exclude_patterns.addAll(preparePatterns(uc.excludesList, recompiler));
+ }
+
+ // do nothing if no update checks were specified
+ if (include_patterns.size() == 0) return;
+
+ // now collect all files in the installation directory and figure
+ // out files to check for deletion
+
+ // use a treeset for fast access
+ TreeSet installed_files = new TreeSet();
+
+ for (Iterator if_it = this.udata.getInstalledFilesList().iterator(); if_it.hasNext();)
+ {
+ String fname = (String) if_it.next();
+
+ File f = new File(fname);
+
+ if (!f.isAbsolute())
+ {
+ f = new File(this.absolute_installpath, fname);
+ }
+
+ installed_files.add(f.getAbsolutePath());
+ }
+
+ // now scan installation directory (breadth first), contains Files of
+ // directories to scan
+ // (note: we'll recurse infinitely if there are circular links or
+ // similar nasty things)
+ Stack scanstack = new Stack();
+
+ // contains File objects determined for deletion
+ ArrayList files_to_delete = new ArrayList();
+
+ try
+ {
+ scanstack.add(absolute_installpath);
+
+ while (!scanstack.empty())
+ {
+ File f = (File) scanstack.pop();
+
+ File[] files = f.listFiles();
+
+ if (files == null) { throw new IOException(f.getPath() + "is not a directory!"); }
+
+ for (int i = 0; i < files.length; i++)
+ {
+ File newf = files[i];
+
+ String newfname = newf.getPath();
+
+ // skip files we just installed
+ if (installed_files.contains(newfname)) continue;
+
+ if (fileMatchesOnePattern(newfname, include_patterns)
+ && (!fileMatchesOnePattern(newfname, exclude_patterns)))
+ {
+ files_to_delete.add(newf);
+ }
+
+ if (newf.isDirectory())
+ {
+ scanstack.push(newf);
+ }
+
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ this.handler.emitError("error while performing update checks", e.toString());
+ }
+
+ for (Iterator f_it = files_to_delete.iterator(); f_it.hasNext();)
+ {
+ File f = (File) f_it.next();
+
+ if (!f.isDirectory())
+ // skip directories - they cannot be removed safely yet
+ {
+// this.handler.emitNotification("deleting " + f.getPath());
+ f.delete();
+ }
+
+ }
+
+ }
+}
+
More information about the izpack-changes
mailing list