KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > izforge > izpack > installer > MultiVolumeUnpacker


1 /*
2  * IzPack - Copyright 2001-2007 Julien Ponge, All Rights Reserved.
3  *
4  * http://www.izforge.com/izpack/ http://developer.berlios.de/projects/izpack/
5  *
6  * Copyright 2007 Dennis Reil
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software distributed under the License
14  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
15  * or implied. See the License for the specific language governing permissions and limitations under
16  * the License.
17  */

18 package com.izforge.izpack.installer;
19
20 import java.io.BufferedOutputStream JavaDoc;
21 import java.io.EOFException JavaDoc;
22 import java.io.File JavaDoc;
23 import java.io.FileInputStream JavaDoc;
24 import java.io.FileOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.io.ObjectInputStream JavaDoc;
28 import java.io.PrintWriter JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.Enumeration JavaDoc;
31 import java.util.HashMap JavaDoc;
32 import java.util.HashSet JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.Properties JavaDoc;
36 import java.util.Stack JavaDoc;
37 import java.util.TreeSet JavaDoc;
38 import java.util.zip.ZipEntry JavaDoc;
39 import java.util.zip.ZipInputStream JavaDoc;
40 import java.util.zip.ZipOutputStream JavaDoc;
41
42 import org.apache.regexp.RE;
43 import org.apache.regexp.RECompiler;
44 import org.apache.regexp.RESyntaxException;
45
46 import com.izforge.izpack.ExecutableFile;
47 import com.izforge.izpack.LocaleDatabase;
48 import com.izforge.izpack.Pack;
49 import com.izforge.izpack.PackFile;
50 import com.izforge.izpack.ParsableFile;
51 import com.izforge.izpack.UpdateCheck;
52 import com.izforge.izpack.XPackFile;
53 import com.izforge.izpack.event.InstallerListener;
54 import com.izforge.izpack.installer.AutomatedInstallData;
55 import com.izforge.izpack.installer.InstallerFrame;
56 import com.izforge.izpack.installer.IzPanel;
57 import com.izforge.izpack.installer.ResourceManager;
58 import com.izforge.izpack.installer.ScriptParser;
59 import com.izforge.izpack.installer.UninstallData;
60 import com.izforge.izpack.io.FileSpanningInputStream;
61 import com.izforge.izpack.io.FileSpanningOutputStream;
62 import com.izforge.izpack.io.VolumeNotFoundException;
63 import com.izforge.izpack.panels.NextMediaDialog;
64 import com.izforge.izpack.util.AbstractUIHandler;
65 import com.izforge.izpack.util.AbstractUIProgressHandler;
66 import com.izforge.izpack.util.Debug;
67 import com.izforge.izpack.util.FileExecutor;
68 import com.izforge.izpack.util.IoHelper;
69 import com.izforge.izpack.util.OsConstraint;
70 import com.izforge.izpack.util.VariableSubstitutor;
71
72 /**
73  * Unpacker class.
74  *
75  * @author Dennis Reil
76  */

77 public class MultiVolumeUnpacker implements IUnpacker
78 {
79
80     /** The installdata. */
81     private AutomatedInstallData idata;
82
83     /** The installer listener. */
84     private AbstractUIProgressHandler handler;
85
86     /** The uninstallation data. */
87     private UninstallData udata;
88
89     /** The variables substitutor. */
90     private VariableSubstitutor vs;
91
92     /** The instances of the unpacker objects. */
93     private static HashMap JavaDoc instances = new HashMap JavaDoc();
94
95     /** The absolute path of the installation. (NOT the canonical!) */
96     private File JavaDoc absolute_installpath;
97
98     /** The packs locale database. */
99     private LocaleDatabase langpack = null;
100
101     /** Interrupt flag if global interrupt is desired. */
102     private static boolean interruptDesired = false;
103
104     /** Do not perform a interrupt call. */
105     private static boolean discardInterrupt = false;
106
107     /** The name of the XML file that specifies the panel langpack */
108     private static final String JavaDoc LANG_FILE_NAME = "packsLang.xml";
109
110     public static final String JavaDoc ALIVE = "alive";
111
112     public static final String JavaDoc INTERRUPT = "doInterrupt";
113
114     public static final String JavaDoc INTERRUPTED = "interruppted";
115
116     /** The result of the operation. */
117     private boolean result = true;
118
119     /**
120      * The constructor.
121      *
122      * @param idata The installation data.
123      * @param handler The installation progress handler.
124      */

125     public MultiVolumeUnpacker(AutomatedInstallData idata, AbstractUIProgressHandler handler)
126     {
127         try
128         {
129             String JavaDoc resource = LANG_FILE_NAME + "_" + idata.localeISO3;
130             this.langpack = new LocaleDatabase(ResourceManager.getInstance().getInputStream(
131                     resource));
132         }
133         catch (Throwable JavaDoc exception)
134         {}
135
136         this.idata = idata;
137         this.handler = handler;
138
139         // Initialize the variable substitutor
140
vs = new VariableSubstitutor(idata.getVariables());
141     }
142
143     /**
144      * Returns a copy of the active unpacker instances.
145      *
146      * @return a copy of active unpacker instances
147      */

148     public static HashMap JavaDoc getRunningInstances()
149     {
150         synchronized (instances)
151         { // Return a shallow copy to prevent a
152
// ConcurrentModificationException.
153
return (HashMap JavaDoc) (instances.clone());
154         }
155     }
156
157     /**
158      * Adds this to the map of all existent instances of Unpacker.
159      */

160     private void addToInstances()
161     {
162         synchronized (instances)
163         {
164             instances.put(this, ALIVE);
165         }
166     }
167
168     /**
169      * Removes this from the map of all existent instances of Unpacker.
170      */

171     private void removeFromInstances()
172     {
173         synchronized (instances)
174         {
175             instances.remove(this);
176         }
177     }
178
179     /**
180      * Initiate interrupt of all alive Unpacker. This method does not interrupt the Unpacker objects
181      * else it sets only the interrupt flag for the Unpacker objects. The dispatching of interrupt
182      * will be performed by the Unpacker objects self.
183      */

184     private static void setInterruptAll()
185     {
186         synchronized (instances)
187         {
188             Iterator JavaDoc iter = instances.keySet().iterator();
189             while (iter.hasNext())
190             {
191                 Object JavaDoc key = iter.next();
192                 if (instances.get(key).equals(ALIVE))
193                 {
194                     instances.put(key, INTERRUPT);
195                 }
196             }
197             // Set global flag to allow detection of it in other classes.
198
// Do not set it to thread because an exec will then be stoped.
199
setInterruptDesired(true);
200         }
201     }
202
203     /**
204      * Initiate interrupt of all alive Unpacker and waits until all Unpacker are interrupted or the
205      * wait time has arrived. If the doNotInterrupt flag in InstallerListener is set to true, the
206      * interrupt will be discarded.
207      *
208      * @param waitTime wait time in millisecounds
209      * @return true if the interrupt will be performed, false if the interrupt will be discarded
210      */

211     public static boolean interruptAll(long waitTime)
212     {
213         long t0 = System.currentTimeMillis();
214         if (isDiscardInterrupt()) return (false);
215         setInterruptAll();
216         while (!isInterruptReady())
217         {
218             if (System.currentTimeMillis() - t0 > waitTime) return (true);
219             try
220             {
221                 Thread.sleep(100);
222             }
223             catch (InterruptedException JavaDoc e)
224             {}
225         }
226         return (true);
227     }
228
229     private static boolean isInterruptReady()
230     {
231         synchronized (instances)
232         {
233             Iterator JavaDoc iter = instances.keySet().iterator();
234             while (iter.hasNext())
235             {
236                 Object JavaDoc key = iter.next();
237                 if (!instances.get(key).equals(INTERRUPTED)) return (false);
238             }
239             return (true);
240         }
241
242     }
243
244     /**
245      * Sets the interrupt flag for this Unpacker to INTERRUPTED if the previos state was INTERRUPT
246      * or INTERRUPTED and returns whether interrupt was initiate or not.
247      *
248      * @return whether interrupt was initiate or not
249      */

250     private boolean performInterrupted()
251     {
252         synchronized (instances)
253         {
254             Object JavaDoc doIt = instances.get(this);
255             if (doIt != null && (doIt.equals(INTERRUPT) || doIt.equals(INTERRUPTED)))
256             {
257                 instances.put(this, INTERRUPTED);
258                 this.result = false;
259                 return (true);
260             }
261             return (false);
262         }
263     }
264
265     /**
266      * Returns whether interrupt was initiate or not for this Unpacker.
267      *
268      * @return whether interrupt was initiate or not
269      */

270     private boolean shouldInterrupt()
271     {
272         synchronized (instances)
273         {
274             Object JavaDoc doIt = instances.get(this);
275             if (doIt != null && (doIt.equals(INTERRUPT) || doIt.equals(INTERRUPTED))) { return (true); }
276             return (false);
277         }
278
279     }
280
281     protected File JavaDoc enterNextMediaMessage(String JavaDoc volumename)
282     {
283         Debug.trace("Enter next media: " + volumename);
284         StackTraceElement JavaDoc[] el = (new Exception JavaDoc()).getStackTrace();
285         for (int i = 0; i < el.length; i++)
286         {
287             StackTraceElement JavaDoc element = el[i];
288             Debug.trace(element.toString());
289         }
290
291         File JavaDoc nextvolume = new File JavaDoc(volumename);
292         NextMediaDialog nmd = null;
293         while (!nextvolume.exists())
294         {
295             if ((this.handler != null) && (this.handler instanceof IzPanel))
296             {
297                 InstallerFrame installframe = ((IzPanel) this.handler).getInstallerFrame();
298                 nmd = new NextMediaDialog(installframe, idata, volumename);
299             }
300             else
301             {
302                 nmd = new NextMediaDialog(null, idata, volumename);
303             }
304             nmd.setVisible(true);
305             String JavaDoc nextmediainput = nmd.getNextMedia();
306             if (nextmediainput != null)
307             {
308                 nextvolume = new File JavaDoc(nextmediainput);
309             }
310             else
311             {
312                 Debug.trace("Input from NextMediaDialog was null");
313                 nextvolume = new File JavaDoc(volumename);
314             }
315         }
316         return nextvolume;
317     }
318
319     /** The run method. */
320     public void run()
321     {
322         addToInstances();
323         try
324         {
325             //
326
// Initialisations
327
FileOutputStream JavaDoc out = null;
328             ArrayList JavaDoc parsables = new ArrayList JavaDoc();
329             ArrayList JavaDoc executables = new ArrayList JavaDoc();
330             ArrayList JavaDoc updatechecks = new ArrayList JavaDoc();
331             List JavaDoc packs = idata.selectedPacks;
332             int npacks = packs.size();
333             Debug.trace("Unpacker starting");
334             handler.startAction("Unpacking", npacks);
335             udata = UninstallData.getInstance();
336             // Custom action listener stuff --- load listeners ----
337
List JavaDoc[] customActions = getCustomActions();
338             // Custom action listener stuff --- beforePacks ----
339
informListeners(customActions, InstallerListener.BEFORE_PACKS, idata, new Integer JavaDoc(
340                     npacks), handler);
341             // vs = new VariableSubstitutor(idata.getVariables());
342
packs = idata.selectedPacks;
343             npacks = packs.size();
344             if (npacks == 0)
345             {
346                 if (performInterrupted())
347                 { // Interrupt was initiated; perform it.
348
return;
349                 }
350
351                 // Custom action listener stuff --- afterPacks ----
352
informListeners(customActions, InstallerListener.AFTER_PACKS, idata, handler, null);
353                 if (performInterrupted())
354                 { // Interrupt was initiated; perform it.
355
return;
356                 }
357
358                 // The end :-)
359
handler.stopAction();
360                 return;
361             }
362             InputStream JavaDoc in = MultiVolumeUnpacker.class
363                     .getResourceAsStream(FileSpanningOutputStream.VOLUMES_INFO);
364             // get volumes metadata
365
ObjectInputStream JavaDoc metadataobj = new ObjectInputStream JavaDoc(in);
366             // TODO: create MetadataObject
367
int volumes = metadataobj.readInt();
368             String JavaDoc volumename = metadataobj.readUTF();
369             Debug.trace("Reading from " + volumes + " volumes with basename " + volumename + " ");
370             metadataobj.close();
371             String JavaDoc mediadirectory = MultiVolumeInstaller.getMediadirectory();
372             if ((mediadirectory == null) || (mediadirectory.length() <= 0))
373             {
374                 Debug.trace("Mediadirectory wasn't set.");
375                 mediadirectory = System.getProperty("java.io.tmpdir"); // try the temporary
376
// directory
377
}
378             Debug.trace("Using mediadirectory = " + mediadirectory);
379             File JavaDoc volume = new File JavaDoc(mediadirectory + File.separator + volumename);
380             if (!volume.exists())
381             {
382                 volume = enterNextMediaMessage(volume.getAbsolutePath());
383             }
384             FileSpanningInputStream fin = new FileSpanningInputStream(volume, volumes);
385
386             // We unpack the selected packs
387
for (int i = 0; i < npacks; i++)
388             {
389                 // We get the pack stream
390
int n = idata.allPacks.indexOf(packs.get(i));
391
392                 in = MultiVolumeUnpacker.class.getResourceAsStream("/packs/pack" + n);
393
394                 // Custom action listener stuff --- beforePack ----
395
informListeners(customActions, InstallerListener.BEFORE_PACK, packs.get(i),
396                         new Integer JavaDoc(npacks), handler);
397                 // find next Entry
398
ObjectInputStream JavaDoc objIn = new ObjectInputStream JavaDoc(in);
399                 // We unpack the files
400
int nfiles = objIn.readInt();
401
402                 // We get the internationalized name of the pack
403
final Pack pack = ((Pack) packs.get(i));
404                 String JavaDoc stepname = pack.name;// the message to be passed to the
405
// installpanel
406
if (langpack != null && !(pack.id == null || "".equals(pack.id)))
407                 {
408
409                     final String JavaDoc name = langpack.getString(pack.id);
410                     if (name != null && !"".equals(name))
411                     {
412                         stepname = name;
413                     }
414                 }
415                 handler.nextStep(stepname, i + 1, nfiles);
416                 for (int j = 0; j < nfiles; j++)
417                 {
418                     // We read the header
419
XPackFile pf = (XPackFile) objIn.readObject();
420
421                     if (OsConstraint.oneMatchesCurrentSystem(pf.osConstraints()))
422                     {
423                         // We translate & build the path
424
String JavaDoc path = IoHelper.translatePath(pf.getTargetPath(), vs);
425                         File JavaDoc pathFile = new File JavaDoc(path);
426                         File JavaDoc dest = pathFile;
427                         if (!pf.isDirectory()) dest = pathFile.getParentFile();
428
429                         if (!dest.exists())
430                         {
431                             // If there are custom actions which would be called
432
// at
433
// creating a directory, create it recursively.
434
List JavaDoc fileListeners = customActions[customActions.length - 1];
435                             if (fileListeners != null && fileListeners.size() > 0)
436                                 mkDirsWithEnhancement(dest, pf, customActions);
437                             else
438                             // Create it in on step.
439
{
440                                 if (!dest.mkdirs())
441                                 {
442                                     handler.emitError("Error creating directories",
443                                             "Could not create directory\n" + dest.getPath());
444                                     handler.stopAction();
445                                     this.result = false;
446                                     return;
447                                 }
448                             }
449                         }
450
451                         if (pf.isDirectory()) continue;
452
453                         // Custom action listener stuff --- beforeFile ----
454
informListeners(customActions, InstallerListener.BEFORE_FILE, pathFile, pf,
455                                 null);
456                         // We add the path to the log,
457
udata.addFile(path);
458
459                         handler.progress(j, path);
460
461                         // if this file exists and should not be overwritten,
462
// check
463
// what to do
464
if ((pathFile.exists()) && (pf.override() != PackFile.OVERRIDE_TRUE))
465                         {
466                             boolean overwritefile = false;
467
468                             // don't overwrite file if the user said so
469
if (pf.override() != PackFile.OVERRIDE_FALSE)
470                             {
471                                 if (pf.override() == PackFile.OVERRIDE_TRUE)
472                                 {
473                                     overwritefile = true;
474                                 }
475                                 else if (pf.override() == PackFile.OVERRIDE_UPDATE)
476                                 {
477                                     // check mtime of involved files
478
// (this is not 100% perfect, because the
479
// already existing file might
480
// still be modified but the new installed
481
// is just a bit newer; we would
482
// need the creation time of the existing
483
// file or record with which mtime
484
// it was installed...)
485
overwritefile = (pathFile.lastModified() < pf.lastModified());
486                                 }
487                                 else
488                                 {
489                                     int def_choice = -1;
490
491                                     if (pf.override() == PackFile.OVERRIDE_ASK_FALSE)
492                                         def_choice = AbstractUIHandler.ANSWER_NO;
493                                     if (pf.override() == PackFile.OVERRIDE_ASK_TRUE)
494                                         def_choice = AbstractUIHandler.ANSWER_YES;
495
496                                     int answer = handler.askQuestion(idata.langpack
497                                             .getString("InstallPanel.overwrite.title")
498                                             + " - " + pathFile.getName(), idata.langpack
499                                             .getString("InstallPanel.overwrite.question")
500                                             + pathFile.getAbsolutePath(),
501                                             AbstractUIHandler.CHOICES_YES_NO, def_choice);
502
503                                     overwritefile = (answer == AbstractUIHandler.ANSWER_YES);
504                                 }
505
506                             }
507
508                             if (!overwritefile)
509                             {
510                                 if (!pf.isBackReference() && !((Pack) packs.get(i)).loose)
511                                 {
512                                     // objIn.skip(pf.length());
513
}
514                                 continue;
515                             }
516
517                         }
518
519                         // We copy the file
520
out = new FileOutputStream JavaDoc(pathFile);
521                         byte[] buffer = new byte[5120];
522                         long bytesCopied = 0;
523                         // InputStream pis = objIn;
524
InputStream JavaDoc pis = fin;
525
526                         if (((Pack) packs.get(i)).loose)
527                         {
528                             pis = new FileInputStream JavaDoc(pf.sourcePath);
529                         }
530
531                         // read in the position of this file
532
// long fileposition = objIn.readLong();
533
long fileposition = pf.getArchivefileposition();
534
535                         while (fin.getFilepointer() < fileposition)
536                         {
537                             // we have to skip some bytes
538
Debug.trace("Skipping bytes to get to file " + pathFile.getName()
539                                     + " (" + fin.getFilepointer() + "<" + fileposition
540                                     + ") target is: " + (fileposition - fin.getFilepointer()));
541                             try
542                             {
543                                 fin.skip(fileposition - fin.getFilepointer());
544                                 break;
545                             }
546                             catch (VolumeNotFoundException vnfe)
547                             {
548                                 File JavaDoc nextmedia = enterNextMediaMessage(vnfe.getVolumename());
549                                 fin.setVolumename(nextmedia.getAbsolutePath());
550                             }
551                         }
552
553                         if (fin.getFilepointer() > fileposition)
554                         {
555                             Debug.trace("Error, can't access file in pack.");
556                         }
557
558                         while (bytesCopied < pf.length())
559                         {
560                             try
561                             {
562                                 if (performInterrupted())
563                                 { // Interrupt was initiated; perform it.
564
out.close();
565                                     if (pis != objIn) pis.close();
566                                     return;
567                                 }
568                                 int maxBytes = (int) Math.min(pf.length() - bytesCopied,
569                                         buffer.length);
570
571                                 int bytesInBuffer = pis.read(buffer, 0, maxBytes);
572                                 if (bytesInBuffer == -1)
573                                 {
574                                     Debug.trace("Unexpected end of stream (installer corrupted?)");
575                                     throw new IOException JavaDoc(
576                                             "Unexpected end of stream (installer corrupted?)");
577                                 }
578
579                                 out.write(buffer, 0, bytesInBuffer);
580
581                                 bytesCopied += bytesInBuffer;
582                             }
583                             catch (VolumeNotFoundException vnfe)
584                             {
585                                 File JavaDoc nextmedia = enterNextMediaMessage(vnfe.getVolumename());
586                                 fin.setVolumename(nextmedia.getAbsolutePath());
587                             }
588                         }
589                         // Cleanings
590
out.close();
591                         // if (pis != objIn) pis.close();
592

593                         // Set file modification time if specified
594
if (pf.lastModified() >= 0) pathFile.setLastModified(pf.lastModified());
595                         // Custom action listener stuff --- afterFile ----
596
informListeners(customActions, InstallerListener.AFTER_FILE, pathFile, pf,
597                                 null);
598                     }
599                     else
600                     {
601                         if (!pf.isBackReference())
602                         {
603                             // objIn.skip(pf.length());
604
}
605                     }
606                 }
607                 // Load information about parsable files
608
int numParsables = objIn.readInt();
609                 Debug.trace("Looking for parsables");
610                 for (int k = 0; k < numParsables; k++)
611                 {
612                     ParsableFile pf = null;
613                     while (true)
614                     {
615                         try
616                         {
617                             pf = (ParsableFile) objIn.readObject();
618                             break;
619                         }
620                         catch (VolumeNotFoundException vnfe)
621                         {
622                             File JavaDoc nextmedia = enterNextMediaMessage(vnfe.getVolumename());
623                             fin.setVolumename(nextmedia.getAbsolutePath());
624                         }
625                         catch (EOFException JavaDoc eofe)
626                         {
627                             File JavaDoc nextmedia = enterNextMediaMessage("");
628                             fin.setVolumename(nextmedia.getAbsolutePath());
629                         }
630                     }
631                     pf.path = IoHelper.translatePath(pf.path, vs);
632                     Debug.trace("Found parsable: " + pf.path);
633                     parsables.add(pf);
634                 }
635
636                 // Load information about executable files
637
int numExecutables = objIn.readInt();
638                 Debug.trace("Looking for executables...");
639                 for (int k = 0; k < numExecutables; k++)
640                 {
641                     ExecutableFile ef = (ExecutableFile) objIn.readObject();
642
643                     ef.path = IoHelper.translatePath(ef.path, vs);
644                     if (null != ef.argList && !ef.argList.isEmpty())
645                     {
646                         String JavaDoc arg = null;
647                         for (int j = 0; j < ef.argList.size(); j++)
648                         {
649                             arg = (String JavaDoc) ef.argList.get(j);
650                             arg = IoHelper.translatePath(arg, vs);
651                             ef.argList.set(j, arg);
652                         }
653                     }
654                     Debug.trace("Found executable: " + ef.path);
655                     executables.add(ef);
656                     if (ef.executionStage == ExecutableFile.UNINSTALL)
657                     {
658                         udata.addExecutable(ef);
659                     }
660                 }
661                 // Custom action listener stuff --- uninstall data ----
662
handleAdditionalUninstallData(udata, customActions);
663
664                 // Load information about updatechecks
665
int numUpdateChecks = objIn.readInt();
666                 Debug.trace("Looking for updatechecks");
667                 for (int k = 0; k < numUpdateChecks; k++)
668                 {
669                     UpdateCheck uc = (UpdateCheck) objIn.readObject();
670                     Debug.trace("found updatecheck");
671                     updatechecks.add(uc);
672                 }
673
674                 // objIn.close();
675

676                 if (performInterrupted())
677                 { // Interrupt was initiated; perform it.
678
return;
679                 }
680
681                 // Custom action listener stuff --- afterPack ----
682
informListeners(customActions, InstallerListener.AFTER_PACK, packs.get(i),
683                         new Integer JavaDoc(i), handler);
684             }
685             Debug.trace("Trying to parse files");
686             // We use the scripts parser
687
ScriptParser parser = new ScriptParser(parsables, vs);
688             parser.parseFiles();
689             Debug.trace("parsed files");
690             if (performInterrupted())
691             { // Interrupt was initiated; perform it.
692
return;
693             }
694             Debug.trace("Trying to execute files");
695             // We use the file executor
696
FileExecutor executor = new FileExecutor(executables);
697             if (executor.executeFiles(ExecutableFile.POSTINSTALL, handler) != 0)
698             {
699                 handler.emitError("File execution failed", "The installation was not completed");
700                 this.result = false;
701                 Debug.trace("File execution failed");
702             }
703
704             if (performInterrupted())
705             { // Interrupt was initiated; perform it.
706
return;
707             }
708             Debug.trace("Create uninstaller");
709             // We put the uninstaller (it's not yet complete...)
710
putUninstaller();
711             Debug.trace("Uninstaller created");
712             // update checks _after_ uninstaller was put, so we don't delete it
713
Debug.trace("Perform updateChecks");
714             performUpdateChecks(updatechecks);
715             Debug.trace("updatechecks performed.");
716             if (performInterrupted())
717             { // Interrupt was initiated; perform it.
718
return;
719             }
720
721             // Custom action listener stuff --- afterPacks ----
722
informListeners(customActions, InstallerListener.AFTER_PACKS, idata, handler, null);
723             if (performInterrupted())
724             { // Interrupt was initiated; perform it.
725
return;
726             }
727             this.writeConfigInformation();
728             // The end :-)
729
handler.stopAction();
730             Debug.trace("Installation complete");
731         }
732         catch (Exception JavaDoc err)
733         {
734             // TODO: finer grained error handling with useful error messages
735
handler.stopAction();
736             handler.emitError("An error occured", err.toString());
737             err.printStackTrace();
738             Debug.trace("Error while installing: " + err.toString());
739             this.result = false;
740         }
741         finally
742         {
743             removeFromInstances();
744         }
745     }
746
747     protected void writeConfigInformation()
748     {
749         // save the variables
750
Properties JavaDoc installerproperties = idata.getVariables();
751         Enumeration JavaDoc installerpropertieskeys = installerproperties.keys();
752         try
753         {
754             String JavaDoc installpath = idata.getVariable("INSTALL_PATH");
755             PrintWriter JavaDoc pw = new PrintWriter JavaDoc(new FileOutputStream JavaDoc(installpath + File.separator
756                     + "installer.properties"));
757             pw.println("# Installer properties, written by MultiVolumeUnpacker.");
758             while (installerpropertieskeys.hasMoreElements())
759             {
760                 String JavaDoc key = (String JavaDoc) installerpropertieskeys.nextElement();
761                 if (key.startsWith("SYSTEM_"))
762                 {
763                     // skip
764
continue;
765                 }
766                 else if (key.startsWith("password_"))
767                 {
768                     // skip
769
continue;
770                 }
771                 pw.println(key + "=" + installerproperties.getProperty(key));
772             }
773             pw.flush();
774             pw.close();
775         }
776         catch (Exception JavaDoc e)
777         {
778             Debug.trace("Error while writing config information in MultiVolumeUnpacker: "
779                     + e.getMessage());
780         }
781     }
782
783     /**
784      * Return the state of the operation.
785      *
786      * @return true if the operation was successful, false otherwise.
787      */

788     public boolean getResult()
789     {
790         return this.result;
791     }
792
793     /**
794      * @param updatechecks
795      */

796     private void performUpdateChecks(ArrayList JavaDoc updatechecks)
797     {
798         ArrayList JavaDoc include_patterns = new ArrayList JavaDoc();
799         ArrayList JavaDoc exclude_patterns = new ArrayList JavaDoc();
800
801         RECompiler recompiler = new RECompiler();
802
803         this.absolute_installpath = new File JavaDoc(idata.getInstallPath()).getAbsoluteFile();
804
805         // at first, collect all patterns
806
for (Iterator JavaDoc iter = updatechecks.iterator(); iter.hasNext();)
807         {
808             UpdateCheck uc = (UpdateCheck) iter.next();
809
810             if (uc.includesList != null)
811                 include_patterns.addAll(preparePatterns(uc.includesList, recompiler));
812
813             if (uc.excludesList != null)
814                 exclude_patterns.addAll(preparePatterns(uc.excludesList, recompiler));
815         }
816
817         // do nothing if no update checks were specified
818
if (include_patterns.size() == 0) return;
819
820         // now collect all files in the installation directory and figure
821
// out files to check for deletion
822

823         // use a treeset for fast access
824
TreeSet JavaDoc installed_files = new TreeSet JavaDoc();
825
826         for (Iterator JavaDoc if_it = this.udata.getFilesList().iterator(); if_it.hasNext();)
827         {
828             String JavaDoc fname = (String JavaDoc) if_it.next();
829
830             File JavaDoc f = new File JavaDoc(fname);
831
832             if (!f.isAbsolute())
833             {
834                 f = new File JavaDoc(this.absolute_installpath, fname);
835             }
836
837             installed_files.add(f.getAbsolutePath());
838         }
839
840         // now scan installation directory (breadth first), contains Files of
841
// directories to scan
842
// (note: we'll recurse infinitely if there are circular links or
843
// similar nasty things)
844
Stack JavaDoc scanstack = new Stack JavaDoc();
845
846         // contains File objects determined for deletion
847
ArrayList JavaDoc files_to_delete = new ArrayList JavaDoc();
848
849         try
850         {
851             scanstack.add(absolute_installpath);
852
853             while (!scanstack.empty())
854             {
855                 File JavaDoc f = (File JavaDoc) scanstack.pop();
856
857                 File JavaDoc[] files = f.listFiles();
858
859                 if (files == null) { throw new IOException JavaDoc(f.getPath() + "is not a directory!"); }
860
861                 for (int i = 0; i < files.length; i++)
862                 {
863                     File JavaDoc newf = files[i];
864
865                     String JavaDoc newfname = newf.getPath();
866
867                     // skip files we just installed
868
if (installed_files.contains(newfname)) continue;
869
870                     if (fileMatchesOnePattern(newfname, include_patterns)
871                             && (!fileMatchesOnePattern(newfname, exclude_patterns)))
872                     {
873                         files_to_delete.add(newf);
874                     }
875
876                     if (newf.isDirectory())
877                     {
878                         scanstack.push(newf);
879                     }
880
881                 }
882             }
883         }
884         catch (IOException JavaDoc e)
885         {
886             this.handler.emitError("error while performing update checks", e.toString());
887         }
888
889         for (Iterator JavaDoc f_it = files_to_delete.iterator(); f_it.hasNext();)
890         {
891             File JavaDoc f = (File JavaDoc) f_it.next();
892
893             if (!f.isDirectory())
894             // skip directories - they cannot be removed safely yet
895
{
896                 this.handler.emitNotification("deleting " + f.getPath());
897                 f.delete();
898             }
899
900         }
901
902     }
903
904     /**
905      * @param filename
906      * @param patterns
907      *
908      * @return true if the file matched one pattern, false if it did not
909      */

910     private boolean fileMatchesOnePattern(String JavaDoc filename, ArrayList JavaDoc patterns)
911     {
912         // first check whether any include matches
913
for (Iterator JavaDoc inc_it = patterns.iterator(); inc_it.hasNext();)
914         {
915             RE pattern = (RE) inc_it.next();
916
917             if (pattern.match(filename)) { return true; }
918         }
919
920         return false;
921     }
922
923     /**
924      * @param list A list of file name patterns (in ant fileset syntax)
925      * @param recompiler The regular expression compiler (used to speed up RE compiling).
926      *
927      * @return List of org.apache.regexp.RE
928      */

929     private List JavaDoc preparePatterns(ArrayList JavaDoc list, RECompiler recompiler)
930     {
931         ArrayList JavaDoc result = new ArrayList JavaDoc();
932
933         for (Iterator JavaDoc iter = list.iterator(); iter.hasNext();)
934         {
935             String JavaDoc element = (String JavaDoc) iter.next();
936
937             if ((element != null) && (element.length() > 0))
938             {
939                 // substitute variables in the pattern
940
element = this.vs.substitute(element, "plain");
941
942                 // check whether the pattern is absolute or relative
943
File JavaDoc f = new File JavaDoc(element);
944
945                 // if it is relative, make it absolute and prepend the
946
// installation path
947
// (this is a bit dangerous...)
948
if (!f.isAbsolute())
949                 {
950                     element = new File JavaDoc(this.absolute_installpath, element).toString();
951                 }
952
953                 // now parse the element and construct a regular expression from
954
// it
955
// (we have to parse it one character after the next because
956
// every
957
// character should only be processed once - it's not possible
958
// to get this
959
// correct using regular expression replacing)
960
StringBuffer JavaDoc element_re = new StringBuffer JavaDoc();
961
962                 int lookahead = -1;
963
964                 int pos = 0;
965
966                 while (pos < element.length())
967                 {
968                     char c;
969
970                     if (lookahead != -1)
971                     {
972                         c = (char) lookahead;
973                         lookahead = -1;
974                     }
975                     else
976                         c = element.charAt(pos++);
977
978                     switch (c)
979                     {
980                     case '/': {
981                         element_re.append(File.separator);
982                         break;
983                     }
984                         // escape backslash and dot
985
case '\\':
986                     case '.': {
987                         element_re.append("\\");
988                         element_re.append(c);
989                         break;
990                     }
991                     case '*': {
992                         if (pos == element.length())
993                         {
994                             element_re.append("[^").append(File.separator).append("]*");
995                             break;
996                         }
997
998                         lookahead = element.charAt(pos++);
999
1000                        // check for "**"
1001
if (lookahead == '*')
1002                        {
1003                            element_re.append(".*");
1004                            // consume second star
1005
lookahead = -1;
1006                        }
1007                        else
1008                        {
1009                            element_re.append("[^").append(File.separator).append("]*");
1010                            // lookahead stays there
1011
}
1012                        break;
1013                    }
1014                    default: {
1015                        element_re.append(c);
1016                        break;
1017                    }
1018                    } // switch
1019

1020                }
1021
1022                // make sure that the whole expression is matched
1023
element_re.append('$');
1024
1025                // replace \ by \\ and create a RE from the result
1026
try
1027                {
1028                    result.add(new RE(recompiler.compile(element_re.toString())));
1029                }
1030                catch (RESyntaxException e)
1031                {
1032                    this.handler.emitNotification("internal error: pattern \"" + element
1033                            + "\" produced invalid RE \"" + f.getPath() + "\"");
1034                }
1035
1036            }
1037        }
1038
1039        return result;
1040    }
1041
1042    /**
1043     * Puts the uninstaller.
1044     *
1045     * @exception Exception Description of the Exception
1046     */

1047    private void putUninstaller() throws Exception JavaDoc
1048    {
1049        // get the uninstaller base, returning if not found so that
1050
// idata.uninstallOutJar remains null
1051
InputStream JavaDoc[] in = new InputStream JavaDoc[2];
1052        in[0] = MultiVolumeUnpacker.class.getResourceAsStream("/res/IzPack.uninstaller");
1053        if (in[0] == null) return;
1054        // The uninstaller extension is facultative; it will be exist only
1055
// if a native library was marked for uninstallation.
1056
in[1] = MultiVolumeUnpacker.class.getResourceAsStream("/res/IzPack.uninstaller-ext");
1057
1058        // Me make the .uninstaller directory
1059
String JavaDoc dest = IoHelper.translatePath("$INSTALL_PATH", vs) + File.separator + "Uninstaller";
1060        String JavaDoc jar = dest + File.separator + idata.info.getUninstallerName();
1061        File JavaDoc pathMaker = new File JavaDoc(dest);
1062        pathMaker.mkdirs();
1063
1064        // We log the uninstaller deletion information
1065
udata.setUninstallerJarFilename(jar);
1066        udata.setUninstallerPath(dest);
1067
1068        // We open our final jar file
1069
FileOutputStream JavaDoc out = new FileOutputStream JavaDoc(jar);
1070        // Intersect a buffer else byte for byte will be written to the file.
1071
BufferedOutputStream JavaDoc bos = new BufferedOutputStream JavaDoc(out);
1072        ZipOutputStream JavaDoc outJar = new ZipOutputStream JavaDoc(bos);
1073        idata.uninstallOutJar = outJar;
1074        outJar.setLevel(9);
1075        udata.addFile(jar);
1076
1077        // We copy the uninstallers
1078
HashSet JavaDoc doubles = new HashSet JavaDoc();
1079
1080        for (int i = 0; i < in.length; ++i)
1081        {
1082            if (in[i] == null) continue;
1083            ZipInputStream JavaDoc inRes = new ZipInputStream JavaDoc(in[i]);
1084            ZipEntry JavaDoc zentry = inRes.getNextEntry();
1085            while (zentry != null)
1086            {
1087                // Puts a new entry, but not twice like META-INF
1088
if (!doubles.contains(zentry.getName()))
1089                {
1090                    doubles.add(zentry.getName());
1091                    outJar.putNextEntry(new ZipEntry JavaDoc(zentry.getName()));
1092
1093                    // Byte to byte copy
1094
int unc = inRes.read();
1095                    while (unc != -1)
1096                    {
1097                        outJar.write(unc);
1098                        unc = inRes.read();
1099                    }
1100
1101                    // Next one please
1102
inRes.closeEntry();
1103                    outJar.closeEntry();
1104                }
1105                zentry = inRes.getNextEntry();
1106            }
1107            inRes.close();
1108        }
1109
1110        // We put the langpack
1111
InputStream JavaDoc in2 = MultiVolumeUnpacker.class.getResourceAsStream("/langpacks/"
1112                + idata.localeISO3 + ".xml");
1113        outJar.putNextEntry(new ZipEntry JavaDoc("langpack.xml"));
1114        int read = in2.read();
1115        while (read != -1)
1116        {
1117            outJar.write(read);
1118            read = in2.read();
1119        }
1120        outJar.closeEntry();
1121    }
1122
1123    // CUSTOM ACTION STUFF -------------- start -----------------
1124

1125    /**
1126     * Informs all listeners which would be informed at the given action type.
1127     *
1128     * @param customActions array of lists with the custom action objects
1129     * @param action identifier for which callback should be called
1130     * @param firstParam first parameter for the call
1131     * @param secondParam second parameter for the call
1132     * @param thirdParam third parameter for the call
1133     */

1134    private void informListeners(List JavaDoc[] customActions, int action, Object JavaDoc firstParam,
1135            Object JavaDoc secondParam, Object JavaDoc thirdParam) throws Exception JavaDoc
1136    {
1137        List JavaDoc listener = null;
1138        // select the right action list.
1139
switch (action)
1140        {
1141        case InstallerListener.BEFORE_FILE:
1142        case InstallerListener.AFTER_FILE:
1143        case InstallerListener.BEFORE_DIR:
1144        case InstallerListener.AFTER_DIR:
1145            listener = customActions[customActions.length - 1];
1146            break;
1147        default:
1148            listener = customActions[0];
1149            break;
1150        }
1151        if (listener == null) return;
1152        // Iterate the action list.
1153
Iterator JavaDoc iter = listener.iterator();
1154        while (iter.hasNext())
1155        {
1156            if (shouldInterrupt()) return;
1157            InstallerListener il = (InstallerListener) iter.next();
1158            switch (action)
1159            {
1160            case InstallerListener.BEFORE_FILE:
1161                il.beforeFile((File JavaDoc) firstParam, (PackFile) secondParam);
1162                break;
1163            case InstallerListener.AFTER_FILE:
1164                il.afterFile((File JavaDoc) firstParam, (PackFile) secondParam);
1165                break;
1166            case InstallerListener.BEFORE_DIR:
1167                il.beforeDir((File JavaDoc) firstParam, (PackFile) secondParam);
1168                break;
1169            case InstallerListener.AFTER_DIR:
1170                il.afterDir((File JavaDoc) firstParam, (PackFile) secondParam);
1171                break;
1172            case InstallerListener.BEFORE_PACK:
1173                il.beforePack((Pack) firstParam, (Integer JavaDoc) secondParam,
1174                        (AbstractUIProgressHandler) thirdParam);
1175                break;
1176            case InstallerListener.AFTER_PACK:
1177                il.afterPack((Pack) firstParam, (Integer JavaDoc) secondParam,
1178                        (AbstractUIProgressHandler) thirdParam);
1179                break;
1180            case InstallerListener.BEFORE_PACKS:
1181                il.beforePacks((AutomatedInstallData) firstParam, (Integer JavaDoc) secondParam,
1182                        (AbstractUIProgressHandler) thirdParam);
1183                break;
1184            case InstallerListener.AFTER_PACKS:
1185                il.afterPacks((AutomatedInstallData) firstParam,
1186                        (AbstractUIProgressHandler) secondParam);
1187                break;
1188
1189            }
1190        }
1191    }
1192
1193    /**
1194     * Returns the defined custom actions split into types including a constructed type for the file
1195     * related installer listeners.
1196     *
1197     * @return array of lists of custom action data like listeners
1198     */

1199    private List JavaDoc[] getCustomActions()
1200    {
1201        String JavaDoc[] listenerNames = AutomatedInstallData.CUSTOM_ACTION_TYPES;
1202        List JavaDoc[] retval = new List JavaDoc[listenerNames.length + 1];
1203        int i;
1204        for (i = 0; i < listenerNames.length; ++i)
1205        {
1206            retval[i] = (List JavaDoc) idata.customData.get(listenerNames[i]);
1207            if (retval[i] == null)
1208            // Make a dummy list, then iterator is ever callable.
1209
retval[i] = new ArrayList JavaDoc();
1210        }
1211        if (retval[AutomatedInstallData.INSTALLER_LISTENER_INDEX].size() > 0)
1212        { // Installer listeners exist
1213
// Create file related installer listener list in the last
1214
// element of custom action array.
1215
i = retval.length - 1; // Should be so, but safe is safe ...
1216
retval[i] = new ArrayList JavaDoc();
1217            Iterator JavaDoc iter = ((List JavaDoc) retval[AutomatedInstallData.INSTALLER_LISTENER_INDEX])
1218                    .iterator();
1219            while (iter.hasNext())
1220            {
1221                // If we get a class cast exception many is wrong and
1222
// we must fix it.
1223
InstallerListener li = (InstallerListener) iter.next();
1224                if (li.isFileListener()) retval[i].add(li);
1225            }
1226
1227        }
1228        return (retval);
1229    }
1230
1231    /**
1232     * Adds additional unistall data to the uninstall data object.
1233     *
1234     * @param udata unistall data
1235     * @param customData array of lists of custom action data like uninstaller listeners
1236     */

1237    private void handleAdditionalUninstallData(UninstallData udata, List JavaDoc[] customData)
1238    {
1239        // Handle uninstall libs
1240
udata.addAdditionalData("__uninstallLibs__",
1241                customData[AutomatedInstallData.UNINSTALLER_LIBS_INDEX]);
1242        // Handle uninstaller listeners
1243
udata.addAdditionalData("uninstallerListeners",
1244                customData[AutomatedInstallData.UNINSTALLER_LISTENER_INDEX]);
1245        // Handle uninstaller jars
1246
udata.addAdditionalData("uninstallerJars",
1247                customData[AutomatedInstallData.UNINSTALLER_JARS_INDEX]);
1248    }
1249
1250    // This method is only used if a file related custom action exist.
1251
/**
1252     * Creates the given directory recursive and calls the method "afterDir" of each listener with
1253     * the current file object and the pack file object. On error an exception is raised.
1254     *
1255     * @param dest the directory which should be created
1256     * @param pf current pack file object
1257     * @param customActions all defined custom actions
1258     * @return false on error, true else
1259     * @throws Exception
1260     */

1261
1262    private boolean mkDirsWithEnhancement(File JavaDoc dest, PackFile pf, List JavaDoc[] customActions)
1263            throws Exception JavaDoc
1264    {
1265        String JavaDoc path = "unknown";
1266        if (dest != null) path = dest.getAbsolutePath();
1267        if (dest != null && !dest.exists() && dest.getParentFile() != null)
1268        {
1269            if (dest.getParentFile().exists())
1270                informListeners(customActions, InstallerListener.BEFORE_DIR, dest, pf, null);
1271            if (!dest.mkdir())
1272            {
1273                mkDirsWithEnhancement(dest.getParentFile(), pf, customActions);
1274                if (!dest.mkdir()) dest = null;
1275            }
1276            informListeners(customActions, InstallerListener.AFTER_DIR, dest, pf, null);
1277        }
1278        if (dest == null)
1279        {
1280            handler.emitError("Error creating directories", "Could not create directory\n" + path);
1281            handler.stopAction();
1282            return (false);
1283        }
1284        return (true);
1285    }
1286
1287    // CUSTOM ACTION STUFF -------------- end -----------------
1288

1289    /**
1290     * Returns whether an interrupt request should be discarded or not.
1291     *
1292     * @return Returns the discard interrupt flag
1293     */

1294    public static synchronized boolean isDiscardInterrupt()
1295    {
1296        return discardInterrupt;
1297    }
1298
1299    /**
1300     * Sets the discard interrupt flag.
1301     *
1302     * @param di the discard interrupt flag to set
1303     */

1304    public static synchronized void setDiscardInterrupt(boolean di)
1305    {
1306        discardInterrupt = di;
1307        setInterruptDesired(false);
1308    }
1309
1310    /**
1311     * Returns the interrupt desired state.
1312     *
1313     * @return the interrupt desired state
1314     */

1315    public static boolean isInterruptDesired()
1316    {
1317        return interruptDesired;
1318    }
1319
1320    /**
1321     * @param interruptDesired The interrupt desired flag to set
1322     */

1323    private static void setInterruptDesired(boolean interruptDesired)
1324    {
1325        MultiVolumeUnpacker.interruptDesired = interruptDesired;
1326    }
1327}
Popular Tags