KickJava   Java API By Example, From Geeks To Geeks.

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


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

22
23 package com.izforge.izpack.installer;
24
25 import java.io.BufferedInputStream JavaDoc;
26 import java.io.BufferedOutputStream JavaDoc;
27 import java.io.File JavaDoc;
28 import java.io.FileInputStream JavaDoc;
29 import java.io.FileNotFoundException JavaDoc;
30 import java.io.FileOutputStream JavaDoc;
31 import java.io.IOException JavaDoc;
32 import java.io.InputStream JavaDoc;
33 import java.io.ObjectInputStream JavaDoc;
34 import java.lang.reflect.Constructor JavaDoc;
35 import java.net.URL JavaDoc;
36 import java.util.ArrayList JavaDoc;
37 import java.util.HashMap JavaDoc;
38 import java.util.HashSet JavaDoc;
39 import java.util.Iterator JavaDoc;
40 import java.util.List JavaDoc;
41 import java.util.Stack JavaDoc;
42 import java.util.TreeSet JavaDoc;
43 import java.util.zip.ZipEntry JavaDoc;
44 import java.util.zip.ZipInputStream JavaDoc;
45 import java.util.zip.ZipOutputStream JavaDoc;
46
47 import org.apache.regexp.RE;
48 import org.apache.regexp.RECompiler;
49 import org.apache.regexp.RESyntaxException;
50
51 import com.izforge.izpack.ExecutableFile;
52 import com.izforge.izpack.LocaleDatabase;
53 import com.izforge.izpack.Pack;
54 import com.izforge.izpack.PackFile;
55 import com.izforge.izpack.ParsableFile;
56 import com.izforge.izpack.UpdateCheck;
57 import com.izforge.izpack.event.InstallerListener;
58 import com.izforge.izpack.util.AbstractUIHandler;
59 import com.izforge.izpack.util.AbstractUIProgressHandler;
60 import com.izforge.izpack.util.FileExecutor;
61 import com.izforge.izpack.util.IoHelper;
62 import com.izforge.izpack.util.OsConstraint;
63 import com.izforge.izpack.util.VariableSubstitutor;
64
65 /**
66  * Unpacker class.
67  *
68  * @author Julien Ponge
69  * @author Johannes Lehtinen
70  */

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

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

143     public static HashMap JavaDoc getRunningInstances()
144     {
145         synchronized (instances)
146         { // Return a shallow copy to prevent a
147
// ConcurrentModificationException.
148
return (HashMap JavaDoc) (instances.clone());
149         }
150     }
151
152     /**
153      * Adds this to the map of all existent instances of Unpacker.
154      */

155     private void addToInstances()
156     {
157         synchronized (instances)
158         {
159             instances.put(this, ALIVE);
160         }
161     }
162
163     /**
164      * Removes this from the map of all existent instances of Unpacker.
165      */

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

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

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

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

265     private boolean shouldInterrupt()
266     {
267         synchronized (instances)
268         {
269             Object JavaDoc doIt = instances.get(this);
270             if (doIt != null && (doIt.equals(INTERRUPT) || doIt.equals(INTERRUPTED))) { return (true); }
271             return (false);
272         }
273
274     }
275
276     /* (non-Javadoc)
277      * @see com.izforge.izpack.installer.IUnpacker#run()
278      */

279     public void run()
280     {
281         addToInstances();
282         try
283         {
284             //
285
// Initialisations
286
FileOutputStream JavaDoc out = null;
287             ArrayList JavaDoc parsables = new ArrayList JavaDoc();
288             ArrayList JavaDoc executables = new ArrayList JavaDoc();
289             ArrayList JavaDoc updatechecks = new ArrayList JavaDoc();
290             List JavaDoc packs = idata.selectedPacks;
291             int npacks = packs.size();
292             handler.startAction("Unpacking", npacks);
293             udata = UninstallData.getInstance();
294             // Custom action listener stuff --- load listeners ----
295
List JavaDoc[] customActions = getCustomActions();
296             // Custom action listener stuff --- beforePacks ----
297
informListeners(customActions, InstallerListener.BEFORE_PACKS, idata, new Integer JavaDoc(
298                     npacks), handler);
299             packs = idata.selectedPacks;
300             npacks = packs.size();
301
302             // We unpack the selected packs
303
for (int i = 0; i < npacks; i++)
304             {
305                 // We get the pack stream
306
int n = idata.allPacks.indexOf(packs.get(i));
307
308                 // Custom action listener stuff --- beforePack ----
309
informListeners(customActions, InstallerListener.BEFORE_PACK, packs.get(i),
310                         new Integer JavaDoc(npacks), handler);
311                 ObjectInputStream JavaDoc objIn = new ObjectInputStream JavaDoc(getPackAsStream(n));
312
313                 // We unpack the files
314
int nfiles = objIn.readInt();
315
316                 // We get the internationalized name of the pack
317
final Pack pack = ((Pack) packs.get(i));
318                 String JavaDoc stepname = pack.name;// the message to be passed to the
319
// installpanel
320
if (langpack != null && !(pack.id == null || "".equals(pack.id)))
321                 {
322
323                     final String JavaDoc name = langpack.getString(pack.id);
324                     if (name != null && !"".equals(name))
325                     {
326                         stepname = name;
327                     }
328                 }
329                 handler.nextStep(stepname, i + 1, nfiles);
330                 for (int j = 0; j < nfiles; j++)
331                 {
332                     // We read the header
333
PackFile pf = (PackFile) objIn.readObject();
334
335                     if (OsConstraint.oneMatchesCurrentSystem(pf.osConstraints()))
336                     {
337                         // We translate & build the path
338
String JavaDoc path = IoHelper.translatePath(pf.getTargetPath(), vs);
339                         File JavaDoc pathFile = new File JavaDoc(path);
340                         File JavaDoc dest = pathFile;
341                         if (!pf.isDirectory()) dest = pathFile.getParentFile();
342
343                         if (!dest.exists())
344                         {
345                             // If there are custom actions which would be called
346
// at
347
// creating a directory, create it recursively.
348
List JavaDoc fileListeners = customActions[customActions.length - 1];
349                             if (fileListeners != null && fileListeners.size() > 0)
350                                 mkDirsWithEnhancement(dest, pf, customActions);
351                             else
352                             // Create it in on step.
353
{
354                                 if (!dest.mkdirs())
355                                 {
356                                     handler.emitError("Error creating directories",
357                                             "Could not create directory\n" + dest.getPath());
358                                     handler.stopAction();
359                                     this.result = false;
360                                     return;
361                                 }
362                             }
363                         }
364
365                         if (pf.isDirectory()) continue;
366
367                         // Custom action listener stuff --- beforeFile ----
368
informListeners(customActions, InstallerListener.BEFORE_FILE, pathFile, pf,
369                                 null);
370                         // We add the path to the log,
371
udata.addFile(path);
372
373                         handler.progress(j, path);
374
375                         // if this file exists and should not be overwritten,
376
// check
377
// what to do
378
if ((pathFile.exists()) && (pf.override() != PackFile.OVERRIDE_TRUE))
379                         {
380                             boolean overwritefile = false;
381
382                             // don't overwrite file if the user said so
383
if (pf.override() != PackFile.OVERRIDE_FALSE)
384                             {
385                                 if (pf.override() == PackFile.OVERRIDE_TRUE)
386                                 {
387                                     overwritefile = true;
388                                 }
389                                 else if (pf.override() == PackFile.OVERRIDE_UPDATE)
390                                 {
391                                     // check mtime of involved files
392
// (this is not 100% perfect, because the
393
// already existing file might
394
// still be modified but the new installed
395
// is just a bit newer; we would
396
// need the creation time of the existing
397
// file or record with which mtime
398
// it was installed...)
399
overwritefile = (pathFile.lastModified() < pf.lastModified());
400                                 }
401                                 else
402                                 {
403                                     int def_choice = -1;
404
405                                     if (pf.override() == PackFile.OVERRIDE_ASK_FALSE)
406                                         def_choice = AbstractUIHandler.ANSWER_NO;
407                                     if (pf.override() == PackFile.OVERRIDE_ASK_TRUE)
408                                         def_choice = AbstractUIHandler.ANSWER_YES;
409
410                                     int answer = handler.askQuestion(idata.langpack
411                                             .getString("InstallPanel.overwrite.title")
412                                             + " - " + pathFile.getName(), idata.langpack
413                                             .getString("InstallPanel.overwrite.question")
414                                             + pathFile.getAbsolutePath(),
415                                             AbstractUIHandler.CHOICES_YES_NO, def_choice);
416
417                                     overwritefile = (answer == AbstractUIHandler.ANSWER_YES);
418                                 }
419
420                             }
421
422                             if (!overwritefile)
423                             {
424                                 if (!pf.isBackReference() && !((Pack) packs.get(i)).loose)
425                                     objIn.skip(pf.length());
426                                 continue;
427                             }
428
429                         }
430
431                         // We copy the file
432
InputStream JavaDoc pis = objIn;
433                         if (pf.isBackReference())
434                         {
435                             InputStream JavaDoc is = getPackAsStream(pf.previousPackNumber);
436                             pis = new ObjectInputStream JavaDoc(is);
437                             // must wrap for blockdata use by objectstream
438
// (otherwise strange result)
439
// skip on underlaying stream (for some reason not
440
// possible on ObjectStream)
441
is.skip(pf.offsetInPreviousPack - 4);
442                             // but the stream header is now already read (== 4
443
// bytes)
444
}
445                         else if (((Pack) packs.get(i)).loose)
446                         {
447                             /* Old way of doing the job by using the (absolute) sourcepath.
448                              * Since this is very likely to fail and does not confirm to the documentation,
449                              * prefer using relative path's
450                             pis = new FileInputStream(pf.sourcePath);
451                              */

452                             
453                             //take the relative path and search for the file
454
//1. look at the location where the "info"-file is loaded from (jar)
455
//2. look into the current working directory
456
//maybe look into other other locations after that (configurable ?)
457

458                             //find directory of jar file
459
URL JavaDoc url = getClass().getResource("/info");
460                             String JavaDoc urlPath = url.getPath();
461                             int pos = urlPath.indexOf('!');
462                             if (pos>=0 && urlPath.startsWith("file:/")){
463                                 //remove jar-specific part
464
urlPath = urlPath.substring("file:/".length(), pos);
465                             }
466                             File JavaDoc installerDir = new File JavaDoc(urlPath);
467                             if (!installerDir.isDirectory())
468                             {
469                                 installerDir = installerDir.getParentFile();
470                             }
471
472                             File JavaDoc resolvedFile = new File JavaDoc(installerDir, pf.getRelativeSourcePath());
473                             if (!resolvedFile.exists()){
474                                 //try alternative destination - the current working directory
475
//user.dir is likely (depends on launcher type) the current directory of the executable or jar-file...
476
final File JavaDoc userDir = new File JavaDoc(System.getProperty("user.dir"));
477                                 resolvedFile = new File JavaDoc(userDir, pf.getRelativeSourcePath());
478                             }
479                             if (resolvedFile.exists()){
480                                 pis = new FileInputStream JavaDoc(resolvedFile);
481                                 //may have a different length & last modified than we had at compiletime, therefore we have to build a new PackFile for the copy process...
482
pf = new PackFile(resolvedFile.getParentFile(), resolvedFile, pf.getTargetPath(), pf.osConstraints(), pf.override(), pf.getAdditionals());
483                             }else{
484                                 //file not found
485
//issue a warning (logging api pending)
486
//since this file was loosely bundled, we continue with the installation.
487
System.out.println("Could not find loosely bundled file: "+pf.getRelativeSourcePath());
488                                 out.close();
489                                 continue;
490                             }
491                         }
492
493                         out = new FileOutputStream JavaDoc(pathFile);
494                         byte[] buffer = new byte[5120];
495                         long bytesCopied = 0;
496                         while (bytesCopied < pf.length())
497                         {
498                             if (performInterrupted())
499                             { // Interrupt was initiated; perform it.
500
out.close();
501                                 if (pis != objIn) pis.close();
502                                 return;
503                             }
504                             int maxBytes = (int) Math.min(pf.length() - bytesCopied, buffer.length);
505                             int bytesInBuffer = pis.read(buffer, 0, maxBytes);
506                             if (bytesInBuffer == -1)
507                                 throw new IOException JavaDoc("Unexpected end of stream (installer corrupted?)");
508
509                             out.write(buffer, 0, bytesInBuffer);
510
511                             bytesCopied += bytesInBuffer;
512                         }
513                         // Cleanings
514
out.close();
515                         if (pis != objIn) pis.close();
516
517                         // Set file modification time if specified
518
if (pf.lastModified() >= 0) pathFile.setLastModified(pf.lastModified());
519                         // Custom action listener stuff --- afterFile ----
520
informListeners(customActions, InstallerListener.AFTER_FILE, pathFile, pf,
521                                 null);
522
523                     }
524                     else
525                     {
526                         if (!pf.isBackReference()) objIn.skip(pf.length());
527                     }
528                 }
529
530                 // Load information about parsable files
531
int numParsables = objIn.readInt();
532                 for (int k = 0; k < numParsables; k++)
533                 {
534                     ParsableFile pf = (ParsableFile) objIn.readObject();
535                     pf.path = IoHelper.translatePath(pf.path, vs);
536                     parsables.add(pf);
537                 }
538
539                 // Load information about executable files
540
int numExecutables = objIn.readInt();
541                 for (int k = 0; k < numExecutables; k++)
542                 {
543                     ExecutableFile ef = (ExecutableFile) objIn.readObject();
544                     ef.path = IoHelper.translatePath(ef.path, vs);
545                     if (null != ef.argList && !ef.argList.isEmpty())
546                     {
547                         String JavaDoc arg = null;
548                         for (int j = 0; j < ef.argList.size(); j++)
549                         {
550                             arg = (String JavaDoc) ef.argList.get(j);
551                             arg = IoHelper.translatePath(arg, vs);
552                             ef.argList.set(j, arg);
553                         }
554                     }
555                     executables.add(ef);
556                     if (ef.executionStage == ExecutableFile.UNINSTALL)
557                     {
558                         udata.addExecutable(ef);
559                     }
560                 }
561                 // Custom action listener stuff --- uninstall data ----
562
handleAdditionalUninstallData(udata, customActions);
563
564                 // Load information about updatechecks
565
int numUpdateChecks = objIn.readInt();
566
567                 for (int k = 0; k < numUpdateChecks; k++)
568                 {
569                     UpdateCheck uc = (UpdateCheck) objIn.readObject();
570
571                     updatechecks.add(uc);
572                 }
573
574                 objIn.close();
575
576                 if (performInterrupted())
577                 { // Interrupt was initiated; perform it.
578
return;
579                 }
580
581                 // Custom action listener stuff --- afterPack ----
582
informListeners(customActions, InstallerListener.AFTER_PACK, packs.get(i),
583                         new Integer JavaDoc(i), handler);
584             }
585
586             // We use the scripts parser
587
ScriptParser parser = new ScriptParser(parsables, vs);
588             parser.parseFiles();
589             if (performInterrupted())
590             { // Interrupt was initiated; perform it.
591
return;
592             }
593
594             // We use the file executor
595
FileExecutor executor = new FileExecutor(executables);
596             if (executor.executeFiles(ExecutableFile.POSTINSTALL, handler) != 0)
597             {
598                 handler.emitError("File execution failed", "The installation was not completed");
599                 this.result = false;
600             }
601
602             if (performInterrupted())
603             { // Interrupt was initiated; perform it.
604
return;
605             }
606
607             // We put the uninstaller (it's not yet complete...)
608
putUninstaller();
609
610             // update checks _after_ uninstaller was put, so we don't delete it
611
performUpdateChecks(updatechecks);
612
613             if (performInterrupted())
614             { // Interrupt was initiated; perform it.
615
return;
616             }
617
618             // Custom action listener stuff --- afterPacks ----
619
informListeners(customActions, InstallerListener.AFTER_PACKS, idata, handler, null);
620             if (performInterrupted())
621             { // Interrupt was initiated; perform it.
622
return;
623             }
624
625             // The end :-)
626
handler.stopAction();
627         }
628         catch (Exception JavaDoc err)
629         {
630             // TODO: finer grained error handling with useful error messages
631
handler.stopAction();
632             handler.emitError("An error occured", err.toString());
633             err.printStackTrace();
634             this.result = false;
635         }
636         finally
637         {
638             removeFromInstances();
639         }
640     }
641
642     /* (non-Javadoc)
643      * @see com.izforge.izpack.installer.IUnpacker#getResult()
644      */

645     public boolean getResult()
646     {
647         return this.result;
648     }
649     
650     /**
651      * @param updatechecks
652      */

653     private void performUpdateChecks(ArrayList JavaDoc updatechecks)
654     {
655         ArrayList JavaDoc include_patterns = new ArrayList JavaDoc();
656         ArrayList JavaDoc exclude_patterns = new ArrayList JavaDoc();
657
658         RECompiler recompiler = new RECompiler();
659
660         this.absolute_installpath = new File JavaDoc(idata.getInstallPath()).getAbsoluteFile();
661
662         // at first, collect all patterns
663
for (Iterator JavaDoc iter = updatechecks.iterator(); iter.hasNext();)
664         {
665             UpdateCheck uc = (UpdateCheck) iter.next();
666
667             if (uc.includesList != null)
668                 include_patterns.addAll(preparePatterns(uc.includesList, recompiler));
669
670             if (uc.excludesList != null)
671                 exclude_patterns.addAll(preparePatterns(uc.excludesList, recompiler));
672         }
673
674         // do nothing if no update checks were specified
675
if (include_patterns.size() == 0) return;
676
677         // now collect all files in the installation directory and figure
678
// out files to check for deletion
679

680         // use a treeset for fast access
681
TreeSet JavaDoc installed_files = new TreeSet JavaDoc();
682
683         for (Iterator JavaDoc if_it = this.udata.getFilesList().iterator(); if_it.hasNext();)
684         {
685             String JavaDoc fname = (String JavaDoc) if_it.next();
686
687             File JavaDoc f = new File JavaDoc(fname);
688
689             if (!f.isAbsolute())
690             {
691                 f = new File JavaDoc(this.absolute_installpath, fname);
692             }
693
694             installed_files.add(f.getAbsolutePath());
695         }
696
697         // now scan installation directory (breadth first), contains Files of
698
// directories to scan
699
// (note: we'll recurse infinitely if there are circular links or
700
// similar nasty things)
701
Stack JavaDoc scanstack = new Stack JavaDoc();
702
703         // contains File objects determined for deletion
704
ArrayList JavaDoc files_to_delete = new ArrayList JavaDoc();
705
706         try
707         {
708             scanstack.add(absolute_installpath);
709
710             while (!scanstack.empty())
711             {
712                 File JavaDoc f = (File JavaDoc) scanstack.pop();
713
714                 File JavaDoc[] files = f.listFiles();
715
716                 if (files == null) { throw new IOException JavaDoc(f.getPath() + "is not a directory!"); }
717
718                 for (int i = 0; i < files.length; i++)
719                 {
720                     File JavaDoc newf = files[i];
721
722                     String JavaDoc newfname = newf.getPath();
723
724                     // skip files we just installed
725
if (installed_files.contains(newfname)) continue;
726
727                     if (fileMatchesOnePattern(newfname, include_patterns)
728                             && (!fileMatchesOnePattern(newfname, exclude_patterns)))
729                     {
730                         files_to_delete.add(newf);
731                     }
732
733                     if (newf.isDirectory())
734                     {
735                         scanstack.push(newf);
736                     }
737
738                 }
739             }
740         }
741         catch (IOException JavaDoc e)
742         {
743             this.handler.emitError("error while performing update checks", e.toString());
744         }
745
746         for (Iterator JavaDoc f_it = files_to_delete.iterator(); f_it.hasNext();)
747         {
748             File JavaDoc f = (File JavaDoc) f_it.next();
749
750             if (!f.isDirectory())
751             // skip directories - they cannot be removed safely yet
752
{
753                 this.handler.emitNotification("deleting " + f.getPath());
754                 f.delete();
755             }
756
757         }
758
759     }
760
761     /**
762      * @param filename
763      * @param patterns
764      *
765      * @return true if the file matched one pattern, false if it did not
766      */

767     private boolean fileMatchesOnePattern(String JavaDoc filename, ArrayList JavaDoc patterns)
768     {
769         // first check whether any include matches
770
for (Iterator JavaDoc inc_it = patterns.iterator(); inc_it.hasNext();)
771         {
772             RE pattern = (RE) inc_it.next();
773
774             if (pattern.match(filename)) { return true; }
775         }
776
777         return false;
778     }
779
780     /**
781      * @param list A list of file name patterns (in ant fileset syntax)
782      * @param recompiler The regular expression compiler (used to speed up RE compiling).
783      *
784      * @return List of org.apache.regexp.RE
785      */

786     private List JavaDoc preparePatterns(ArrayList JavaDoc list, RECompiler recompiler)
787     {
788         ArrayList JavaDoc result = new ArrayList JavaDoc();
789
790         for (Iterator JavaDoc iter = list.iterator(); iter.hasNext();)
791         {
792             String JavaDoc element = (String JavaDoc) iter.next();
793
794             if ((element != null) && (element.length() > 0))
795             {
796                 // substitute variables in the pattern
797
element = this.vs.substitute(element, "plain");
798
799                 // check whether the pattern is absolute or relative
800
File JavaDoc f = new File JavaDoc(element);
801
802                 // if it is relative, make it absolute and prepend the
803
// installation path
804
// (this is a bit dangerous...)
805
if (!f.isAbsolute())
806                 {
807                     element = new File JavaDoc(this.absolute_installpath, element).toString();
808                 }
809
810                 // now parse the element and construct a regular expression from
811
// it
812
// (we have to parse it one character after the next because
813
// every
814
// character should only be processed once - it's not possible
815
// to get this
816
// correct using regular expression replacing)
817
StringBuffer JavaDoc element_re = new StringBuffer JavaDoc();
818
819                 int lookahead = -1;
820
821                 int pos = 0;
822
823                 while (pos < element.length())
824                 {
825                     char c;
826
827                     if (lookahead != -1)
828                     {
829                         c = (char) lookahead;
830                         lookahead = -1;
831                     }
832                     else
833                         c = element.charAt(pos++);
834
835                     switch (c)
836                     {
837                     case '/': {
838                         element_re.append(File.separator);
839                         break;
840                     }
841                     // escape backslash and dot
842
case '\\':
843                     case '.': {
844                         element_re.append("\\");
845                         element_re.append(c);
846                         break;
847                     }
848                     case '*': {
849                         if (pos == element.length())
850                         {
851                             element_re.append("[^").append(File.separator).append("]*");
852                             break;
853                         }
854
855                         lookahead = element.charAt(pos++);
856
857                         // check for "**"
858
if (lookahead == '*')
859                         {
860                             element_re.append(".*");
861                             // consume second star
862
lookahead = -1;
863                         }
864                         else
865                         {
866                             element_re.append("[^").append(File.separator).append("]*");
867                             // lookahead stays there
868
}
869                         break;
870                     }
871                     default: {
872                         element_re.append(c);
873                         break;
874                     }
875                     } // switch
876

877                 }
878
879                 // make sure that the whole expression is matched
880
element_re.append('$');
881
882                 // replace \ by \\ and create a RE from the result
883
try
884                 {
885                     result.add(new RE(recompiler.compile(element_re.toString())));
886                 }
887                 catch (RESyntaxException e)
888                 {
889                     this.handler.emitNotification("internal error: pattern \"" + element
890                             + "\" produced invalid RE \"" + f.getPath() + "\"");
891                 }
892
893             }
894         }
895
896         return result;
897     }
898
899     /**
900      * Puts the uninstaller.
901      *
902      * @exception Exception Description of the Exception
903      */

904     private void putUninstaller() throws Exception JavaDoc
905     {
906         // get the uninstaller base, returning if not found so that
907
// idata.uninstallOutJar remains null
908
InputStream JavaDoc[] in = new InputStream JavaDoc[2];
909         in[0] = Unpacker.class.getResourceAsStream("/res/IzPack.uninstaller");
910         if (in[0] == null) return;
911         // The uninstaller extension is facultative; it will be exist only
912
// if a native library was marked for uninstallation.
913
in[1] = Unpacker.class.getResourceAsStream("/res/IzPack.uninstaller-ext");
914
915         // Me make the .uninstaller directory
916
String JavaDoc dest = IoHelper.translatePath("$INSTALL_PATH", vs) + File.separator + "Uninstaller";
917         String JavaDoc jar = dest + File.separator + idata.info.getUninstallerName();
918         File JavaDoc pathMaker = new File JavaDoc(dest);
919         pathMaker.mkdirs();
920
921         // We log the uninstaller deletion information
922
udata.setUninstallerJarFilename(jar);
923         udata.setUninstallerPath(dest);
924
925         // We open our final jar file
926
FileOutputStream JavaDoc out = new FileOutputStream JavaDoc(jar);
927         // Intersect a buffer else byte for byte will be written to the file.
928
BufferedOutputStream JavaDoc bos = new BufferedOutputStream JavaDoc(out);
929         ZipOutputStream JavaDoc outJar = new ZipOutputStream JavaDoc(bos);
930         idata.uninstallOutJar = outJar;
931         outJar.setLevel(9);
932         udata.addFile(jar);
933
934         // We copy the uninstallers
935
HashSet JavaDoc doubles = new HashSet JavaDoc();
936
937         for (int i = 0; i < in.length; ++i)
938         {
939             if (in[i] == null) continue;
940             ZipInputStream JavaDoc inRes = new ZipInputStream JavaDoc(in[i]);
941             ZipEntry JavaDoc zentry = inRes.getNextEntry();
942             while (zentry != null)
943             {
944                 // Puts a new entry, but not twice like META-INF
945
if (!doubles.contains(zentry.getName()))
946                 {
947                     doubles.add(zentry.getName());
948                     outJar.putNextEntry(new ZipEntry JavaDoc(zentry.getName()));
949
950                     // Byte to byte copy
951
int unc = inRes.read();
952                     while (unc != -1)
953                     {
954                         outJar.write(unc);
955                         unc = inRes.read();
956                     }
957
958                     // Next one please
959
inRes.closeEntry();
960                     outJar.closeEntry();
961                 }
962                 zentry = inRes.getNextEntry();
963             }
964             inRes.close();
965         }
966
967         // We put the langpack
968
InputStream JavaDoc in2 = Unpacker.class.getResourceAsStream("/langpacks/" + idata.localeISO3
969                 + ".xml");
970         outJar.putNextEntry(new ZipEntry JavaDoc("langpack.xml"));
971         int read = in2.read();
972         while (read != -1)
973         {
974             outJar.write(read);
975             read = in2.read();
976         }
977         outJar.closeEntry();
978     }
979
980     /**
981      * Returns a stream to a pack, location depending on if it's web based.
982      *
983      * @param n The pack number.
984      * @return The stream or null if it could not be found.
985      * @exception Exception Description of the Exception
986      */

987     private InputStream JavaDoc getPackAsStream(int n) throws Exception JavaDoc
988     {
989         InputStream JavaDoc in = null;
990
991         String JavaDoc webDirURL = idata.info.getWebDirURL();
992
993         if (webDirURL == null) // local
994
{
995             in = Unpacker.class.getResourceAsStream("/packs/pack" + n);
996         }
997         else
998         // web based
999
{
1000            // TODO: Look first in same directory as primary jar
1001
// This may include prompting for changing of media
1002
// TODO: download and cache them all before starting copy process
1003

1004            // See compiler.Packager#getJarOutputStream for the counterpart
1005
String JavaDoc baseName = idata.info.getInstallerBase();
1006            String JavaDoc packURL = webDirURL + "/" + baseName + ".pack" + n + ".jar";
1007            URL JavaDoc url = new URL JavaDoc("jar:" + packURL + "!/packs/pack" + n);
1008            // JarURLConnection jarConnection = (JarURLConnection)
1009
// url.openConnection();
1010
// TODO: what happens when using an automated installer?
1011
in = new WebAccessor(null).openInputStream(url);
1012            // TODO: Fails miserably when pack jars are not found, so this is
1013
// temporary
1014
if (in == null) throw new FileNotFoundException JavaDoc(url.toString());
1015        }
1016        if( in != null && idata.info.getPackDecoderClassName() != null )
1017        {
1018            Class JavaDoc decoder = Class.forName(idata.info.getPackDecoderClassName());
1019            Class JavaDoc[] paramsClasses = new Class JavaDoc[1];
1020            paramsClasses[0] = Class.forName("java.io.InputStream");
1021            Constructor JavaDoc constructor = decoder.getDeclaredConstructor(paramsClasses);
1022            // Our first used decoder input stream (bzip2) reads byte for byte from
1023
// the source. Therefore we put a buffering stream between it and the
1024
// source.
1025
InputStream JavaDoc buffer = new BufferedInputStream JavaDoc(in);
1026            Object JavaDoc[] params = { buffer };
1027            Object JavaDoc instance = null;
1028            instance = constructor.newInstance( params);
1029            if (!InputStream JavaDoc.class.isInstance(instance))
1030                throw new InstallerException( "'" + idata.info.getPackDecoderClassName()
1031                        + "' must be derived from "
1032                        + InputStream JavaDoc.class.toString());
1033            in = (InputStream JavaDoc) instance;
1034
1035        }
1036        return in;
1037    }
1038
1039    // CUSTOM ACTION STUFF -------------- start -----------------
1040

1041    /**
1042     * Informs all listeners which would be informed at the given action type.
1043     *
1044     * @param customActions array of lists with the custom action objects
1045     * @param action identifier for which callback should be called
1046     * @param firstParam first parameter for the call
1047     * @param secondParam second parameter for the call
1048     * @param thirdParam third parameter for the call
1049     */

1050    private void informListeners(List JavaDoc[] customActions, int action, Object JavaDoc firstParam,
1051            Object JavaDoc secondParam, Object JavaDoc thirdParam) throws Exception JavaDoc
1052    {
1053        List JavaDoc listener = null;
1054        // select the right action list.
1055
switch (action)
1056        {
1057        case InstallerListener.BEFORE_FILE:
1058        case InstallerListener.AFTER_FILE:
1059        case InstallerListener.BEFORE_DIR:
1060        case InstallerListener.AFTER_DIR:
1061            listener = customActions[customActions.length - 1];
1062            break;
1063        default:
1064            listener = customActions[0];
1065            break;
1066        }
1067        if (listener == null) return;
1068        // Iterate the action list.
1069
Iterator JavaDoc iter = listener.iterator();
1070        while (iter.hasNext())
1071        {
1072            if (shouldInterrupt()) return;
1073            InstallerListener il = (InstallerListener) iter.next();
1074            switch (action)
1075            {
1076            case InstallerListener.BEFORE_FILE:
1077                il.beforeFile((File JavaDoc) firstParam, (PackFile) secondParam);
1078                break;
1079            case InstallerListener.AFTER_FILE:
1080                il.afterFile((File JavaDoc) firstParam, (PackFile) secondParam);
1081                break;
1082            case InstallerListener.BEFORE_DIR:
1083                il.beforeDir((File JavaDoc) firstParam, (PackFile) secondParam);
1084                break;
1085            case InstallerListener.AFTER_DIR:
1086                il.afterDir((File JavaDoc) firstParam, (PackFile) secondParam);
1087                break;
1088            case InstallerListener.BEFORE_PACK:
1089                il.beforePack((Pack) firstParam, (Integer JavaDoc) secondParam,
1090                        (AbstractUIProgressHandler) thirdParam);
1091                break;
1092            case InstallerListener.AFTER_PACK:
1093                il.afterPack((Pack) firstParam, (Integer JavaDoc) secondParam,
1094                        (AbstractUIProgressHandler) thirdParam);
1095                break;
1096            case InstallerListener.BEFORE_PACKS:
1097                il.beforePacks((AutomatedInstallData) firstParam, (Integer JavaDoc) secondParam,
1098                        (AbstractUIProgressHandler) thirdParam);
1099                break;
1100            case InstallerListener.AFTER_PACKS:
1101                il.afterPacks((AutomatedInstallData) firstParam,
1102                        (AbstractUIProgressHandler) secondParam);
1103                break;
1104
1105            }
1106        }
1107    }
1108
1109    /**
1110     * Returns the defined custom actions split into types including a constructed type for the file
1111     * related installer listeners.
1112     *
1113     * @return array of lists of custom action data like listeners
1114     */

1115    private List JavaDoc[] getCustomActions()
1116    {
1117        String JavaDoc[] listenerNames = AutomatedInstallData.CUSTOM_ACTION_TYPES;
1118        List JavaDoc[] retval = new List JavaDoc[listenerNames.length + 1];
1119        int i;
1120        for (i = 0; i < listenerNames.length; ++i)
1121        {
1122            retval[i] = (List JavaDoc) idata.customData.get(listenerNames[i]);
1123            if (retval[i] == null)
1124            // Make a dummy list, then iterator is ever callable.
1125
retval[i] = new ArrayList JavaDoc();
1126        }
1127        if (retval[AutomatedInstallData.INSTALLER_LISTENER_INDEX].size() > 0)
1128        { // Installer listeners exist
1129
// Create file related installer listener list in the last
1130
// element of custom action array.
1131
i = retval.length - 1; // Should be so, but safe is safe ...
1132
retval[i] = new ArrayList JavaDoc();
1133            Iterator JavaDoc iter = ((List JavaDoc) retval[AutomatedInstallData.INSTALLER_LISTENER_INDEX])
1134                    .iterator();
1135            while (iter.hasNext())
1136            {
1137                // If we get a class cast exception many is wrong and
1138
// we must fix it.
1139
InstallerListener li = (InstallerListener) iter.next();
1140                if (li.isFileListener()) retval[i].add(li);
1141            }
1142
1143        }
1144        return (retval);
1145    }
1146
1147    /**
1148     * Adds additional unistall data to the uninstall data object.
1149     *
1150     * @param udata unistall data
1151     * @param customData array of lists of custom action data like uninstaller listeners
1152     */

1153    private void handleAdditionalUninstallData(UninstallData udata, List JavaDoc[] customData)
1154    {
1155        // Handle uninstall libs
1156
udata.addAdditionalData("__uninstallLibs__",
1157                customData[AutomatedInstallData.UNINSTALLER_LIBS_INDEX]);
1158        // Handle uninstaller listeners
1159
udata.addAdditionalData("uninstallerListeners",
1160                customData[AutomatedInstallData.UNINSTALLER_LISTENER_INDEX]);
1161        // Handle uninstaller jars
1162
udata.addAdditionalData("uninstallerJars",
1163                customData[AutomatedInstallData.UNINSTALLER_JARS_INDEX]);
1164    }
1165
1166    // This method is only used if a file related custom action exist.
1167
/**
1168     * Creates the given directory recursive and calls the method "afterDir" of each listener with
1169     * the current file object and the pack file object. On error an exception is raised.
1170     *
1171     * @param dest the directory which should be created
1172     * @param pf current pack file object
1173     * @param customActions all defined custom actions
1174     * @return false on error, true else
1175     * @throws Exception
1176     */

1177
1178    private boolean mkDirsWithEnhancement(File JavaDoc dest, PackFile pf, List JavaDoc[] customActions)
1179            throws Exception JavaDoc
1180    {
1181        String JavaDoc path = "unknown";
1182        if (dest != null) path = dest.getAbsolutePath();
1183        if (dest != null && !dest.exists() && dest.getParentFile() != null)
1184        {
1185            if (dest.getParentFile().exists())
1186                informListeners(customActions, InstallerListener.BEFORE_DIR, dest, pf, null);
1187            if (!dest.mkdir())
1188            {
1189                mkDirsWithEnhancement(dest.getParentFile(), pf, customActions);
1190                if (!dest.mkdir()) dest = null;
1191            }
1192            informListeners(customActions, InstallerListener.AFTER_DIR, dest, pf, null);
1193        }
1194        if (dest == null)
1195        {
1196            handler.emitError("Error creating directories", "Could not create directory\n" + path);
1197            handler.stopAction();
1198            return (false);
1199        }
1200        return (true);
1201    }
1202
1203    // CUSTOM ACTION STUFF -------------- end -----------------
1204

1205    /**
1206     * Returns whether an interrupt request should be discarded or not.
1207     *
1208     * @return Returns the discard interrupt flag
1209     */

1210    public static synchronized boolean isDiscardInterrupt()
1211    {
1212        return discardInterrupt;
1213    }
1214
1215    /**
1216     * Sets the discard interrupt flag.
1217     *
1218     * @param di the discard interrupt flag to set
1219     */

1220    public static synchronized void setDiscardInterrupt(boolean di)
1221    {
1222        discardInterrupt = di;
1223        setInterruptDesired(false);
1224    }
1225
1226    /**
1227     * Returns the interrupt desired state.
1228     *
1229     * @return the interrupt desired state
1230     */

1231    public static boolean isInterruptDesired()
1232    {
1233        return interruptDesired;
1234    }
1235
1236    /**
1237     * @param interruptDesired The interrupt desired flag to set
1238     */

1239    private static void setInterruptDesired(boolean interruptDesired)
1240    {
1241        Unpacker.interruptDesired = interruptDesired;
1242    }
1243}
1244
Popular Tags