All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.izforge.izpack.installer.unpacker.UnpackerBase Maven / Gradle / Ivy

There is a newer version: 5.2.3
Show newest version
/*
 * IzPack - Copyright 2001-2012 Julien Ponge, All Rights Reserved.
 *
 * http://izpack.org/
 * http://izpack.codehaus.org/
 *
 * Copyright 2007 Dennis Reil
 * Copyright 2012 Tim Anderson
 *
 * 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.unpacker;

import com.izforge.izpack.api.data.*;
import com.izforge.izpack.api.event.InstallerListener;
import com.izforge.izpack.api.event.ProgressListener;
import com.izforge.izpack.api.exception.InstallerException;
import com.izforge.izpack.api.exception.IzPackException;
import com.izforge.izpack.api.exception.ResourceInterruptedException;
import com.izforge.izpack.api.handler.AbstractPrompt;
import com.izforge.izpack.api.handler.AbstractUIHandler;
import com.izforge.izpack.api.handler.Prompt;
import com.izforge.izpack.api.resource.Messages;
import com.izforge.izpack.api.resource.Resources;
import com.izforge.izpack.api.rules.RulesEngine;
import com.izforge.izpack.api.substitutor.VariableSubstitutor;
import com.izforge.izpack.core.handler.ProgressHandler;
import com.izforge.izpack.core.handler.PromptUIHandler;
import com.izforge.izpack.core.resource.ResourceManager;
import com.izforge.izpack.installer.bootstrap.Installer;
import com.izforge.izpack.installer.data.UninstallData;
import com.izforge.izpack.installer.event.InstallerListeners;
import com.izforge.izpack.installer.util.PackHelper;
import com.izforge.izpack.util.*;
import com.izforge.izpack.util.file.DirectoryScanner;
import com.izforge.izpack.util.file.GlobPatternMapper;
import com.izforge.izpack.util.file.types.FileSet;
import com.izforge.izpack.util.os.FileQueue;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.izforge.izpack.api.handler.Prompt.*;
import static com.izforge.izpack.installer.bootstrap.Installer.INSTALLER_AUTO;


/**
 * Abstract base class for all unpacker implementations.
 *
 * @author Dennis Reil, 
 * @author Tim Anderson
 */
public abstract class UnpackerBase implements IUnpacker
{
    /**
     * The logger.
     */
    private static Logger logger = Logger.getLogger(UnpackerBase.class.getName());

    /**
     * Path to resources in jar
     */
    public static final String RESOURCES_PATH = "resources/";

    /**
     * The installation data.
     */
    private final InstallData installData;

    private List selectedPacks;

    /**
     * The uninstallation data.
     */
    private final UninstallData uninstallData;

    /**
     * The pack resources.
     */
    private final PackResources resources;

    /**
     * The rules engine.
     */
    private final RulesEngine rules;

    /**
     * The variables.
     */
    private final Variables variables;

    /**
     * Translations
     */
    private final Messages messages;

    /**
     * The variable replacer.
     */
    private final VariableSubstitutor variableSubstitutor;

    /**
     * The file queue factory.
     */
    private final FileQueueFactory queueFactory;

    /**
     * The housekeeper.
     */
    private final Housekeeper housekeeper;

    /**
     * The listeners.
     */
    private final InstallerListeners listeners;

    /**
     * The installer listener.
     */
    private ProgressListener listener;

    /**
     * The prompt.
     */
    private final Prompt prompt;

    /**
     * The platform-model matcher.
     */
    private final PlatformModelMatcher matcher;

    /**
     * The result of the operation.
     */
    private boolean result = true;

    /**
     * Determines if unpack operations should be cancelled.
     */
    private final Cancellable cancellable;

    /**
     * The unpacking state.
     */
    private enum State
    {
        READY, UNPACKING, INTERRUPT, INTERRUPTED
    }

    /**
     * The current unpacking state.
     */
    private State state = State.READY;

    /**
     * If true, prevent interrupts.
     */
    private boolean disableInterrupt = false;

    /**
     * Translation cache for packs
     */
    private Messages packMessages;

    /**
     * Constructs an UnpackerBase.
     *
     * @param installData         the installation data
     * @param resources           the pack resources
     * @param rules               the rules engine
     * @param variableSubstitutor the variable substituter
     * @param uninstallData       the uninstallation data
     * @param factory             the file queue factory
     * @param housekeeper         the housekeeper
     * @param listeners           the listeners
     * @param prompt              the prompt
     * @param matcher             the platform-model matcher
     */
    public UnpackerBase(InstallData installData, PackResources resources, RulesEngine rules,
                        VariableSubstitutor variableSubstitutor, UninstallData uninstallData, FileQueueFactory factory,
                        Housekeeper housekeeper, InstallerListeners listeners, Prompt prompt,
                        PlatformModelMatcher matcher)
    {
        this.installData = installData;
        this.resources = resources;
        this.rules = rules;
        this.variableSubstitutor = variableSubstitutor;
        this.uninstallData = uninstallData;
        this.queueFactory = factory;
        this.housekeeper = housekeeper;
        this.listeners = listeners;
        this.prompt = prompt;
        this.matcher = matcher;
        this.variables = installData.getVariables();
        this.messages = installData.getMessages();
        cancellable = new Cancellable()
        {
            @Override
            public boolean isCancelled()
            {
                return isInterrupted();
            }
        };
    }

    /**
     * Sets the progress listener.
     *
     * @param listener the progress listener
     */
    @Override
    public void setProgressListener(ProgressListener listener)
    {
        this.listener = listener;
    }

    /**
     * Runs the unpacker.
     */
    @Override
    public void run()
    {
        resetLogging();
        unpack();
    }

    private void logIntro()
    {
        final String startMessage = messages.get("installer.started");
        char[] chars = new char[startMessage.length()];
        Arrays.fill(chars, '=');
        logger.info(new String(chars));
        logger.info(startMessage);

        URLClassLoader cl = (URLClassLoader) getClass().getClassLoader();
        InputStream is = null;
        try
        {
            URL url = cl.findResource("META-INF/MANIFEST.MF");
            is = url.openStream();
            Manifest manifest = new Manifest(is);
            Attributes attr = manifest.getMainAttributes();
            logger.info(messages.get("installer.version", attr.getValue("Created-By")));
        }
        catch (IOException e)
        {
            logger.log(Level.WARNING, "IzPack version not found in manifest", e);
        }
        finally
        {
            IOUtils.closeQuietly(is);
        }

        logger.info(messages.get("installer.platform", matcher.getCurrentPlatform()));
    }

    private void logEpilog()
    {
        logger.info(messages.get("installer.finished"));
    }

    /**
     * Unpacks the installation files.
     */
    public void unpack()
    {
        logIntro();

        state = State.UNPACKING;
        ObjectInputStream objIn = null;
        try
        {
            FileQueue queue = queueFactory.isSupported() ? queueFactory.create() : null;

            InputStream in = resources.getInputStream("packs.info");
            objIn = new ObjectInputStream(in);
            @SuppressWarnings("unchecked") List packsInfo = (List) objIn.readObject();
            objIn.close();

            selectedPacks = installData.getSelectedPacks();

            preUnpack(selectedPacks);
            unpack(packsInfo, queue);
            postUnpack(selectedPacks, queue);
        }
        catch (Exception exception)
        {
            setResult(false);
            logger.log(Level.SEVERE, exception.getMessage(), exception);

            listener.stopAction();

            if (exception instanceof ResourceInterruptedException)
            {
                prompt.message(Type.INFORMATION, messages.get("installer.cancelled"));
            } else
            {
                IzPackException ize;
                if (exception instanceof InstallerException)
                {
                    InstallerException ie = (InstallerException) exception;
                    Throwable t = ie.getCause();
                    ize = new IzPackException(messages.get("installer.errorMessage"),
                            t != null ? t : exception);
                } else if (exception instanceof IzPackException)
                {
                    ize = (IzPackException) exception;
                } else
                {
                    ize = new IzPackException(exception.getMessage(), exception);
                }
                switch (ize.getPromptType())
                {
                    case ERROR:
                        prompt.message(ize);
                        break;

                    case WARNING:
                        AbstractUIHandler handler = new PromptUIHandler(prompt);
                        if (handler.askWarningQuestion(null,
                                AbstractPrompt.getThrowableMessage(ize) + "\n" + messages.get("installer.continueQuestion"),
                                AbstractUIHandler.CHOICES_YES_NO,
                                AbstractUIHandler.ANSWER_NO)
                                == AbstractUIHandler.ANSWER_YES)
                        {
                            return;
                        }
                        break;

                    default:
                        break;
                }
            }

            housekeeper.shutDown(4);
        }
        finally
        {
            cleanup();
            logEpilog();
            IOUtils.closeQuietly(objIn);
        }
    }

    /**
     * Return the state of the operation.
     *
     * @return true if the operation was successful, false otherwise.
     */
    public boolean getResult()
    {
        return result;
    }

    /**
     * Interrupts the unpacker, and waits for it to complete.
     * 

* If interrupts have been prevented ({@link #isInterruptDisabled} returns true), then this * returns immediately. * * @param timeout the maximum time to wait, in milliseconds * @return true if the interrupt will be performed, false if the interrupt will be discarded */ @Override public boolean interrupt(long timeout) { boolean result; if (isInterruptDisabled()) { result = false; } else { synchronized (this) { if (state != State.READY && state != State.INTERRUPTED) { state = State.INTERRUPT; try { wait(timeout); } catch (InterruptedException ignore) { // do nothing } result = state == State.INTERRUPTED; } else { result = true; } } } return result; } /** * Determines if interrupts should be disabled. * * @param disable if true disable interrupts, otherwise enable them */ @Override public synchronized void setDisableInterrupt(boolean disable) { if (state == State.INTERRUPT || state == State.INTERRUPTED) { throw new IllegalStateException("Cannot disable interrupts. Unpacking has already been interrupted"); } disableInterrupt = disable; } /** * Determines if interrupts have been disabled or not. * * @return true if interrupts have been disabled, otherwise false */ public synchronized boolean isInterruptDisabled() { return disableInterrupt; } /** * Invoked prior to unpacking. *

* This notifies the {@link ProgressListener}, and any registered {@link InstallerListener listeners}. * * @param packs the packs to unpack * @throws InstallerException for any error */ protected void preUnpack(List packs) throws InstallerException { logger.fine("Unpacker starting"); listener.startAction("Unpacking", packs.size()); listeners.beforePacks(packs, listener); } /** * Unpacks the selected packs. * * @param packs the packs to unpack * @param queue the file queue, or {@code null} if queuing is not supported * @throws ResourceInterruptedException if unpacking is cancelled * @throws InstallerException for any error */ protected void unpack(List packs, FileQueue queue) throws InstallerException { int count = packs.size(); for (int i = 0; i < count; i++) { PackInfo packInfo = packs.get(i); Pack pack = packInfo.getPack(); if (shouldUnpack(pack)) { List parsables = new ArrayList(); List executables = new ArrayList(); List updateChecks = new ArrayList(); listeners.beforePack(pack, i); unpack(packInfo, i, queue, parsables, executables, updateChecks); checkInterrupt(); logger.fine("Found " + parsables.size() + " parsable files"); parseFiles(parsables); checkInterrupt(); logger.fine("Found " + executables.size() + " executable files"); executeFiles(executables); checkInterrupt(); // update checks should be done _after_ uninstaller was put, so we don't delete it. TODO performUpdateChecks(updateChecks); checkInterrupt(); listeners.afterPack(pack); } } } /** * Unpacks a pack. * * @param packInfo the pack info of the current pack * @param packNo the pack number * @param queue the file queue, or {@code null} if queuing is not supported * @throws IzPackException for any error */ protected void unpack(PackInfo packInfo, int packNo, FileQueue queue, List parsables, List executables, List updateChecks) { InputStream in = null; Pack pack = packInfo.getPack(); PackFile[] packFiles = packInfo.getPackFiles().toArray(new PackFile[]{}); try { int len = packFiles.length; String stepName = getStepName(pack); selectedPacks = installData.getSelectedPacks(); listener.nextStep(stepName, selectedPacks.indexOf(pack) + 1, len); in = resources.getPackStream(pack.getName()); for (int i = 0; i < len; i++) { PackFile packFile = packFiles[i]; final boolean isDirectory = packFile.isDirectory(); logger.fine("Unpacking " + (isDirectory?"directory":"file") + " " + packFile.getTargetPath() + " (backreference: " + packFile.isBackReference() + ")"); if (shouldUnpack(packFile)) { // unpack the file unpack(packFile, in, i + 1, pack, queue); } else { if (!isDirectory) { // condition is not fulfilled, so skip it in main stream skip(packFile, pack, in); } } } readParsableFiles(packInfo, parsables); readExecutableFiles(packInfo, executables); readUpdateChecks(packInfo, updateChecks); } catch (IzPackException exception) { throw exception; } catch (Exception exception) { throw new InstallerException("Failed to unpack pack: " + pack.getName(), exception); } finally { IOUtils.closeQuietly(in); } } /** * Determines if a file should be unpacked. * * @param file the file to check * @return {@code true} if the file should be unpacked; {@code false} if it should be skipped */ private boolean shouldUnpack(PackFile file) { boolean result = true; if (file.hasCondition()) { result = isConditionTrue(file.getCondition()); } if (result && file.osConstraints() != null && !file.osConstraints().isEmpty()) { result = matcher.matchesCurrentPlatform(file.osConstraints()); } return result; } /** * Unpacks a pack file. * * @param packFile the pack file * @param packInputStream the pack file input stream * @param fileNo the pack file number * @param pack the pack that the pack file comes from * @param queue the file queue, or {@code null} if queuing is not supported * @throws IOException for any I/O error * @throws IzPackException for any other error */ protected void unpack(PackFile packFile, InputStream packInputStream, int fileNo, Pack pack, FileQueue queue) throws IOException { String targetPath = packFile.getTargetPath(); // translate & build the path String path = IoHelper.translatePath(targetPath, variables); File target = new File(path); File dir = target; if (!packFile.isDirectory()) { dir = target.getParentFile(); } createDirectory(dir, packFile, pack); // Add path to the log getUninstallData().addFile(path, pack.isUninstall()); if (packFile.isDirectory()) { return; } listeners.beforeFile(target, packFile, pack); listener.progress(fileNo, path); // if this file exists and should not be overwritten, check what to do if (target.exists() && (packFile.override() != OverrideType.OVERRIDE_TRUE) && !isOverwriteFile(packFile, target)) { if (!packFile.isBackReference() && !pack.isLoose() && !packFile.isPack200Jar()) { long size = packFile.size(); logger.fine("|- No overwrite - skipping pack stream by " + size + " bytes"); skip(packInputStream, size); } } else { handleOverrideRename(packFile, target); extract(packFile, target, packInputStream, pack, queue); } } /** * Extracts a pack file. * * @param packFile the pack file * @param target the file to write to * @param packInputStream the pack file input stream * @param pack the pack that the pack file comes from * @param queue the file queue, or {@code null} if queuing is not supported * @throws IOException for any I/O error * @throws ResourceInterruptedException if installation is cancelled * @throws IzPackException for any IzPack error */ protected void extract(PackFile packFile, File target, InputStream packInputStream, Pack pack, FileQueue queue) throws IOException { InputStream packStream = null; try { FileUnpacker unpacker; if (!pack.isLoose() && packFile.isBackReference()) { PackFile linkedPackFile = packFile.getLinkedPackFile(); packStream = resources.getInputStream(ResourceManager.RESOURCE_BASEPATH_DEFAULT + linkedPackFile.getStreamResourceName()); if (!packFile.isPack200Jar()) { // Non-Pack200 files are saved in main pack stream // Offset is always 0 for Pack200 resources, because each file has its own stream resource long size = linkedPackFile.getStreamOffset(); logger.fine("|- Backreference to pack stream (offset: " + size + " bytes"); skip(packStream, size); } } else if (packFile.isPack200Jar()) { packStream = resources.getInputStream(ResourceManager.RESOURCE_BASEPATH_DEFAULT + packFile.getStreamResourceName()); } else { packStream = new NoCloseInputStream(packInputStream); } unpacker = createFileUnpacker(packFile, pack, queue, cancellable); logger.fine("|- Extracting file using " + unpacker.getClass().getName() + ")"); unpacker.unpack(packFile, packStream, target); checkInterrupt(); if (!unpacker.isQueued()) { listeners.afterFile(target, packFile, pack); } } finally { if (!(packStream instanceof NoCloseInputStream)) { IOUtils.closeQuietly(packStream); } } } /** * Skips a pack file. * * @param packFile the pack file * @param pack the pack * @param packInputStream the pack stream * @throws IOException if the file cannot be skipped */ protected void skip(PackFile packFile, Pack pack, InputStream packInputStream) throws IOException { if (!pack.isLoose() && !packFile.isBackReference() && !packFile.isPack200Jar()) { long size = packFile.size(); logger.fine("|- Condition not fulfilled - skipping pack stream " + packFile.getTargetPath() + " by " + size + " bytes "); skip(packInputStream, packFile.size()); } } /** * Creates an unpacker to unpack a pack file. * * @param file the pack file to unpack * @param pack the parent pack * @param queue the file queue. May be {@code null} * @param cancellable determines if the unpacker should be cancelled * @return the unpacker * @throws InstallerException for any installer error */ protected FileUnpacker createFileUnpacker(PackFile file, Pack pack, FileQueue queue, Cancellable cancellable) throws InstallerException { PackCompression compressionFormat = getInstallData().getInfo().getCompressionFormat(); FileUnpacker unpacker; if (pack.isLoose()) { unpacker = new LooseFileUnpacker(cancellable, queue, prompt); } else if (file.isPack200Jar()) { unpacker = new Pack200FileUnpacker(cancellable, resources, queue); } else if (compressionFormat != PackCompression.DEFAULT) { unpacker = new CompressedFileUnpacker(cancellable, queue, compressionFormat); } else { unpacker = new DefaultFileUnpacker(cancellable, queue); } return unpacker; } /** * Invoked after each pack has been unpacked. * * @param packs the packs * @param queue the file queue, or {@code null} if queuing is not supported * @throws ResourceInterruptedException if installation is cancelled * @throws IOException for any I/O error */ protected void postUnpack(List packs, FileQueue queue) throws IOException, InstallerException { InstallData installData = getInstallData(); // commit the file queue if there are potentially blocked files if (queue != null && !queue.isEmpty()) { queue.execute(); installData.setRebootNecessary(queue.isRebootNecessary()); } checkInterrupt(); listeners.afterPacks(packs, listener); checkInterrupt(); // write installation information writeInstallationInformation(); // unpacking complete listener.stopAction(); } /** * Invoked after unpacking has completed, in order to clean up. */ protected void cleanup() { state = State.READY; } /** * Returns the installation data. * * @return the installation data */ protected InstallData getInstallData() { return installData; } /** * Returns the uninstallation data. * * @return the uninstallation data */ protected UninstallData getUninstallData() { return uninstallData; } /** * Returns the pack resources. * * @return the pack resources */ protected PackResources getResources() { return resources; } /** * Returns the variable replacer. * * @return the variable replacer */ protected VariableSubstitutor getVariableSubstitutor() { return variableSubstitutor; } /** * Returns the prompt. * * @return the prompt */ protected Prompt getPrompt() { return prompt; } /** * Determines if a pack should be unpacked. * * @param pack the pack * @return true if the pack should be unpacked, false if it should be skipped */ protected boolean shouldUnpack(Pack pack) { return selectedPacks.contains(pack) && (!pack.hasCondition() || rules.isConditionTrue(pack.getCondition())); } /** * Sets the result of the unpacking operation. * * @param result if true denotes success */ protected void setResult(boolean result) { this.result = result; } protected boolean isConditionTrue(String id) { return rules.isConditionTrue(id); } /** * Returns the step name for a pack, for reporting purposes. * * @param pack the pack * @return the pack's step name */ protected String getStepName(Pack pack) { if (packMessages == null) { if (messages != null) { try { packMessages = messages.newMessages(Resources.PACK_TRANSLATIONS_RESOURCE_NAME); } catch (Exception ex) { logger.fine(ex.getLocalizedMessage()); } } } // hide pack name if it is hidden return pack.isHidden() ? "" : PackHelper.getPackName(pack, packMessages); } /** * Creates a directory including any necessary but nonexistent parent directories, associated with a pack file. *

* If {@link InstallerListener}s are registered, these will be notified for each directory created. * * @param dir the directory to create * @param file the pack file * @param pack the pack that {@code file} comes from * @throws IzPackException if the directory cannot be created or a listener throws an exception */ protected void createDirectory(File dir, PackFile file, Pack pack) { if (!dir.exists()) { if (!listeners.isFileListener()) { // Create it in one step. if (!dir.mkdirs()) { throw new IzPackException("Could not create directory: " + dir.getPath()); } } else { File parent = dir.getParentFile(); if (parent != null) { createDirectory(parent, file, pack); } listeners.beforeDir(dir, file, pack); if (!dir.mkdir()) { throw new IzPackException("Could not create directory: " + dir.getPath()); } listeners.afterDir(dir, file, pack); } } } /** * Parses {@link ParsableFile} instances collected during unpacking. * * @param files the files to parse * @throws InstallerException if parsing fails * @throws ResourceInterruptedException if installation is interrupted */ private void parseFiles(List files) { if (!files.isEmpty()) { ScriptParser parser = new ScriptParser(getVariableSubstitutor(), matcher); for (ParsableFile file : files) { try { parser.parse(file); } catch (Exception exception) { throw new InstallerException("Failed to parse: " + file.getPath(), exception); } checkInterrupt(); } } } /** * Runs {@link ExecutableFile} instances collected during unpacking. * * @param executables the executables to run * @throws InstallerException if an executable fails */ private void executeFiles(List executables) { if (!executables.isEmpty()) { FileExecutor executor = new FileExecutor(executables); PromptUIHandler handler = new ProgressHandler(listener, prompt); if (executor.executeFiles(ExecutableFile.POSTINSTALL, matcher, handler) != 0) { throw new InstallerException("File execution failed"); } } } /** * Determines if the unpacker has been interrupted. * * @return true if the unpacker has been interrupted, otherwise false */ protected synchronized boolean isInterrupted() { boolean result = false; if (state == State.INTERRUPT) { setResult(false); state = State.INTERRUPTED; result = true; notifyAll(); // notify threads waiting in interrupt() } else { if (state == State.INTERRUPTED) { result = true; } } return result; } /** * Throws an {@link ResourceInterruptedException} if installation has been interrupted. * * @throws ResourceInterruptedException if installation is interrupted */ protected void checkInterrupt() { if (isInterrupted()) { throw new ResourceInterruptedException("Installation cancelled"); } } /** * Performs update checks. * * @param checks the update checks. May be {@code null} * @throws IzPackException for any error */ protected void performUpdateChecks(List checks) { if (checks != null && !checks.isEmpty()) { logger.info("Cleaning up the target folder ..."); File absoluteInstallPath = new File(installData.getInstallPath()).getAbsoluteFile(); FileSet fileset = new FileSet(); List filesToDelete = new ArrayList(); List dirsToDelete = new ArrayList(); try { fileset.setDir(absoluteInstallPath); for (UpdateCheck check : checks) { if (check.includesList != null) { for (String include : check.includesList) { fileset.createInclude().setName(variableSubstitutor.substitute(include)); } } if (check.excludesList != null) { for (String exclude : check.excludesList) { fileset.createExclude().setName(variableSubstitutor.substitute(exclude)); } } } DirectoryScanner scanner = fileset.getDirectoryScanner(); scanner.scan(); String[] srcFiles = scanner.getIncludedFiles(); String[] srcDirs = scanner.getIncludedDirectories(); Set installedFiles = new TreeSet(); for (String name : uninstallData.getInstalledFilesList()) { File file = new File(name); if (!file.isAbsolute()) { file = new File(absoluteInstallPath, name); } installedFiles.add(file); } for (String srcFile : srcFiles) { File newFile = new File(scanner.getBasedir(), srcFile); // skip files we just installed if (!installedFiles.contains(newFile)) { filesToDelete.add(newFile); } } for (String srcDir : srcDirs) { // All directories except INSTALL_PATH if (!srcDir.isEmpty()) { File newDir = new File(scanner.getBasedir(), srcDir); // skip directories we just installed if (!installedFiles.contains(newDir)) { dirsToDelete.add(newDir); } } } } catch (IzPackException exception) { throw exception; } catch (Exception exception) { throw new IzPackException(exception); } for (File f : filesToDelete) { if (!f.delete()) { logger.warning("Cleanup: Unable to delete file " + f); } else { logger.fine("Cleanup: Deleted file " + f); } } // Sort directories, deepest path first to be able to // delete recursively Collections.sort(dirsToDelete); Collections.reverse(dirsToDelete); for (File d : dirsToDelete) { if (!d.exists()) { break; } // Don't try to delete non-empty directories, because they // probably must have been implicitly created as parents // of regular installation files File[] files = d.listFiles(); if (files != null && files.length != 0) { break; } // Only empty directories will be deleted if (!d.delete()) { logger.warning("Cleanup: Unable to delete directory " + d); } else { logger.fine("Cleanup: Deleted directory " + d); } } } } /** * Writes information about the installed packs and the variables at installation time. * * @throws InstallerException for any installer error * @throws IOException for any I/O error */ protected void writeInstallationInformation() throws IOException { if (!installData.getInfo().isWriteInstallationInformation()) { logger.fine("Skip writing installation information"); return; } logger.fine("Writing installation information"); String installDir = installData.getInstallPath(); List installedPacks = new ArrayList(selectedPacks); File installationInfo = new File(installDir + File.separator + InstallData.INSTALLATION_INFORMATION); if (!installationInfo.exists()) { logger.fine("Creating info file " + installationInfo.getAbsolutePath()); File dir = new File(installData.getInstallPath()); if (!dir.exists()) { // if no packs have been installed, then the installation directory won't exist if (!dir.mkdirs()) { throw new InstallerException("Failed to create directory: " + dir); } } if (!installationInfo.createNewFile()) { throw new InstallerException("Failed to create file: " + installationInfo); } } else { logger.fine("Previous installation information found"); // read in old information and update FileInputStream fin = new FileInputStream(installationInfo); ObjectInputStream oin = new ObjectInputStream(fin); List packs; try { //noinspection unchecked packs = (List) oin.readObject(); } catch (Exception exception) { throw new InstallerException("Failed to read previous installation information", exception); } finally { IOUtils.closeQuietly(oin); IOUtils.closeQuietly(fin); } installedPacks.addAll(packs); } FileOutputStream fout = new FileOutputStream(installationInfo); ObjectOutputStream oout = new ObjectOutputStream(fout); oout.writeObject(installedPacks); oout.writeObject(variables.getProperties()); logger.fine("Writing installation information finished"); IOUtils.closeQuietly(oout); IOUtils.closeQuietly(fout); uninstallData.addFile(installationInfo.getAbsolutePath(), true); } /** * Skips bytes in a stream. * * @param stream the stream * @param bytes the no. of bytes to skip * @throws IOException for any I/O error, or if the no. of bytes skipped doesn't match that expected */ protected void skip(InputStream stream, long bytes) throws IOException { long skipped = stream.skip(bytes); if (skipped != bytes) { throw new IOException("Expected to skip: " + bytes + " in stream but skipped: " + skipped); } } /** * Determines if a file should be overwritten. * * @param pf the pack file * @param file the file to check * @return {@code true} if the file should be overwritten */ protected boolean isOverwriteFile(PackFile pf, File file) { boolean result = false; // don't overwrite file if the user said so if (pf.override() != OverrideType.OVERRIDE_FALSE) { if (pf.override() == OverrideType.OVERRIDE_TRUE) { result = true; } else { if (pf.override() == OverrideType.OVERRIDE_UPDATE) { // check mtime of involved files // (this is not 100% perfect, because the // already existing file might // still be modified but the new installed // is just a bit newer; we would // need the creation time of the existing // file or record with which mtime // it was installed...) result = (file.lastModified() < pf.lastModified()); } else { Option defChoice = null; if (pf.override() == OverrideType.OVERRIDE_ASK_FALSE) { defChoice = Option.NO; } else if (pf.override() == OverrideType.OVERRIDE_ASK_TRUE) { defChoice = Option.YES; } // are we running in automated mode? If so use default choice. if (Installer.getInstallerMode() == INSTALLER_AUTO) { result = (defChoice == Option.YES); } else // ask the user { Option answer = prompt.confirm(Type.QUESTION, messages.get("InstallPanel.overwrite.title") + " - " + file.getName(), messages.get("InstallPanel.overwrite.question") + file.getAbsolutePath(), Options.YES_NO, defChoice); result = (answer == Option.YES); } } } } return result; } /** * Renames a file, if it exists and the pack file defines how it should be handled. * * @param pf the pack file * @param file the file to rename * @throws InstallerException if the file cannot be renamed */ protected void handleOverrideRename(PackFile pf, File file) { if (file.exists() && pf.overrideRenameTo() != null) { GlobPatternMapper mapper = new GlobPatternMapper(); mapper.setFrom("*"); mapper.setTo(pf.overrideRenameTo()); mapper.setCaseSensitive(true); String[] newFileNameArr = mapper.mapFileName(file.getName()); if (newFileNameArr != null) { String newFileName = newFileNameArr[0]; File newPathFile = new File(file.getParent(), newFileName); if (newPathFile.exists()) { if (!newPathFile.delete()) { logger.warning("Failed to delete: " + newPathFile); } } if (!file.renameTo(newPathFile)) { throw new InstallerException("The file " + file + " could not be renamed to " + newPathFile); } } else { throw new InstallerException("File name " + file.getName() + " cannot be mapped using the expression \"" + pf.overrideRenameTo() + "\""); } } } /** * Initializes {@link ParsableFile parseable files} according to the current environment. * * @param packInfo the pack info fpor the current pack * @param parsables used to collect the read objects */ protected void readParsableFiles(PackInfo packInfo, List parsables) { for (ParsableFile parsableFile : packInfo.getParsables()) { logger.fine("Unpacked parsable: " + parsableFile.toString()); if (!parsableFile.hasCondition() || isConditionTrue(parsableFile.getCondition())) { String path = IoHelper.translatePath(parsableFile.getPath(), variables); parsableFile.setPath(path); parsables.add(parsableFile); } } } /** * Initializes {@link ExecutableFile executable files} according to the current environment. * * @param packInfo the pack info fpor the current pack * @param executables used to collect the read objects */ protected void readExecutableFiles(PackInfo packInfo, List executables) { for (ExecutableFile executableFile : packInfo.getExecutables()) { logger.fine("Unpacked executable: " + executableFile.toString()); if (!executableFile.hasCondition() || isConditionTrue(executableFile.getCondition())) { executableFile.path = IoHelper.translatePath(executableFile.path, variables); if (null != executableFile.argList && !executableFile.argList.isEmpty()) { for (int j = 0; j < executableFile.argList.size(); j++) { String arg = executableFile.argList.get(j); arg = IoHelper.translatePath(arg, variables); executableFile.argList.set(j, arg); } } executables.add(executableFile); if (executableFile.executionStage == ExecutableFile.UNINSTALL) { uninstallData.addExecutable(executableFile); } } } } /** * Initializes {@link UpdateCheck update checks} according to the current environment. * * @param packInfo the pack info fpor the current pack * @param updateChecks used to collect the read objects */ protected void readUpdateChecks(PackInfo packInfo, List updateChecks) { updateChecks.addAll(packInfo.getUpdateChecks()); } private void resetLogging() { try { LogUtils.loadConfiguration(ResourceManager.getInstallLoggingConfigurationResourceName(), variables); } catch (IOException e) { throw new IzPackException(e, Type.WARNING); } logger = Logger.getLogger(UnpackerBase.class.getName()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy