001/*
002 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved.
003 * 
004 * http://www.izforge.com/izpack/
005 * http://developer.berlios.de/projects/izpack/
006 * 
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 * 
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *     
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020package com.izforge.izpack.uninstaller;
021
022import java.io.BufferedReader;
023import java.io.File;
024import java.io.InputStream;
025import java.io.InputStreamReader;
026import java.io.ObjectInputStream;
027import java.util.ArrayList;
028import java.util.Collections;
029import java.util.Iterator;
030import java.util.List;
031import java.util.TreeSet;
032
033import com.izforge.izpack.ExecutableFile;
034import com.izforge.izpack.event.UninstallerListener;
035import com.izforge.izpack.util.AbstractUIProgressHandler;
036import com.izforge.izpack.util.FileExecutor;
037
038/**
039 * The files destroyer class.
040 * 
041 * @author Julien Ponge
042 */
043public class Destroyer extends Thread
044{
045
046    /** True if the destroyer must force the recursive deletion. */
047    private boolean forceDestroy;
048
049    /** The installation path. */
050    private String installPath;
051
052    /** the destroyer listener. */
053    private AbstractUIProgressHandler handler;
054
055    /**
056     * The constructor.
057     * 
058     * @param installPath The installation path.
059     * @param forceDestroy Shall we force the recursive deletion.
060     * @param handler The destroyer listener.
061     */
062    public Destroyer(String installPath, boolean forceDestroy, AbstractUIProgressHandler handler)
063    {
064        super("IzPack - Destroyer");
065
066        this.installPath = installPath;
067        this.forceDestroy = forceDestroy;
068        this.handler = handler;
069    }
070
071    /** The run method. */
072    public void run()
073    {
074        try
075        {
076            // We get the list of uninstaller listeners
077            List[] listeners = getListenerLists();
078            // We get the list of the files to delete
079            ArrayList executables = getExecutablesList();
080
081            FileExecutor executor = new FileExecutor(executables);
082            executor.executeFiles(ExecutableFile.UNINSTALL, this.handler);
083
084            ArrayList files = getFilesList();
085            int size = files.size();
086
087            // Custem action listener stuff --- beforeDeletion ----
088            informListeners(listeners[0], UninstallerListener.BEFORE_DELETION, files, handler);
089
090            handler.startAction("destroy", size);
091
092            // We destroy the files
093            for (int i = 0; i < size; i++)
094            {
095                File file = (File) files.get(i);
096                // Custem action listener stuff --- beforeDelete ----
097                informListeners(listeners[1], UninstallerListener.BEFORE_DELETE, file, handler);
098
099                file.delete();
100
101                // Custem action listener stuff --- afterDelete ----
102                informListeners(listeners[1], UninstallerListener.AFTER_DELETE, file, handler);
103
104                handler.progress(i, file.getAbsolutePath());
105            }
106
107            // Custem action listener stuff --- afterDeletion ----
108            informListeners(listeners[0], UninstallerListener.AFTER_DELETION, files, handler);
109
110            // We make a complementary cleanup
111            handler.progress(size, "[ cleanups ]");
112            cleanup(new File(installPath));
113
114            handler.stopAction();
115        }
116        catch (Exception err)
117        {
118            handler.stopAction();
119            err.printStackTrace();
120            handler.emitError("exception caught", err.toString());
121        }
122    }
123
124    /**
125     * Asks the JVM for the uninstaller deletion.
126     * 
127     * @exception Exception Description of the Exception
128     */
129//    private void askUninstallerRemoval() throws Exception
130//    {
131//        // Initialisations
132//        InputStream in = Destroyer.class.getResourceAsStream("/jarlocation.log");
133//        InputStreamReader inReader = new InputStreamReader(in);
134//        BufferedReader reader = new BufferedReader(inReader);
135//
136//        // We delete
137//        File jar = new File(reader.readLine());
138//        File path = new File(reader.readLine());
139//        File inst = new File(installPath);
140//        jar.deleteOnExit();
141//        path.deleteOnExit();
142//        inst.deleteOnExit();
143//    }
144
145    /**
146     * Returns an ArrayList of the files to delete.
147     * 
148     * @return The files list.
149     * @exception Exception Description of the Exception
150     */
151    private ArrayList getFilesList() throws Exception
152    {
153        // Initialisations
154        TreeSet files = new TreeSet(Collections.reverseOrder());
155        InputStream in = Destroyer.class.getResourceAsStream("/install.log");
156        InputStreamReader inReader = new InputStreamReader(in);
157        BufferedReader reader = new BufferedReader(inReader);
158
159        // We skip the first line (the installation path)
160        reader.readLine();
161
162        // We read it
163        String read = reader.readLine();
164        while (read != null)
165        {
166            files.add(new File(read));
167            read = reader.readLine();
168        }
169
170        // We return it
171        return new ArrayList(files);
172    }
173
174    private ArrayList getExecutablesList() throws Exception
175    {
176        ArrayList executables = new ArrayList();
177        ObjectInputStream in = new ObjectInputStream(Destroyer.class
178                .getResourceAsStream("/executables"));
179        int num = in.readInt();
180        for (int i = 0; i < num; i++)
181        {
182            ExecutableFile file = (ExecutableFile) in.readObject();
183            executables.add(file);
184        }
185        return executables;
186    }
187
188    /**
189     * Makes some reccursive cleanups.
190     * 
191     * @param file The file to wipe.
192     * @exception Exception Description of the Exception
193     */
194    private void cleanup(File file) throws Exception
195    {
196        if (file.isDirectory())
197        {
198            File[] files = file.listFiles();
199            int size = files.length;
200            for (int i = 0; i < size; i++)
201                cleanup(files[i]);
202            file.delete();
203        }
204        else if (forceDestroy) file.delete();
205
206    }
207
208    // CUSTOM ACTION STUFF -------------- start -----------------
209
210    /**
211     * Load the defined uninstall listener objects.
212     * 
213     * @return a list with the defined uninstall listeners
214     * @throws Exception
215     */
216    private List[] getListenerLists() throws Exception
217    {
218        ArrayList[] uninstaller = new ArrayList[] { new ArrayList(), new ArrayList()};
219        // Load listeners if exist
220        InputStream in;
221        ObjectInputStream objIn;
222        in = Destroyer.class.getResourceAsStream("/uninstallerListeners");
223        if (in != null)
224        {
225            objIn = new ObjectInputStream(in);
226            List listeners = (List) objIn.readObject();
227            objIn.close();
228            Iterator iter = listeners.iterator();
229            while (iter != null && iter.hasNext())
230            {
231                Class clazz = Class.forName(((String) iter.next()));
232                UninstallerListener ul = (UninstallerListener) clazz.newInstance();
233                if (ul.isFileListener()) uninstaller[1].add(ul);
234                uninstaller[0].add(ul);
235            }
236        }
237        return uninstaller;
238    }
239
240    /**
241     * Informs all listeners.
242     * 
243     * @param listeners list with the listener objects
244     * @param action identifier which callback should be called
245     * @param param parameter for the call
246     * @param handler the current progress handler
247     */
248
249    private void informListeners(List listeners, int action, Object param,
250            AbstractUIProgressHandler handler)
251    {
252        // Iterate the action list.
253        Iterator iter = listeners.iterator();
254        UninstallerListener il = null;
255        while (iter.hasNext())
256        {
257            try
258            {
259                il = (UninstallerListener) iter.next();
260                switch (action)
261                {
262                case UninstallerListener.BEFORE_DELETION:
263                    il.beforeDeletion((List) param, handler);
264                    break;
265                case UninstallerListener.AFTER_DELETION:
266                    il.afterDeletion((List) param, handler);
267                    break;
268                case UninstallerListener.BEFORE_DELETE:
269                    il.beforeDelete((File) param, handler);
270                    break;
271                case UninstallerListener.AFTER_DELETE:
272                    il.afterDelete((File) param, handler);
273                    break;
274                }
275            }
276            catch (Throwable e)
277            { // Catch it to prevent for a block of uninstallation.
278                handler.emitError("Skipping custom action because exception caught during "
279                        + il.getClass().getName(), e.toString());
280            }
281        }
282    }
283
284    // CUSTOM ACTION STUFF -------------- end -----------------
285
286}