001: /*
002: * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
003: *
004: * http://izpack.org/
005: * http://izpack.codehaus.org/
006: *
007: * Copyright 2007 Dennis Reil
008: *
009: * Licensed under the Apache License, Version 2.0 (the "License");
010: * you may not use this file except in compliance with the License.
011: * You may obtain a copy of the License at
012: *
013: * http://www.apache.org/licenses/LICENSE-2.0
014: *
015: * Unless required by applicable law or agreed to in writing, software
016: * distributed under the License is distributed on an "AS IS" BASIS,
017: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018: * See the License for the specific language governing permissions and
019: * limitations under the License.
020: */
021: package com.izforge.izpack.installer;
022:
023: import java.io.BufferedOutputStream;
024: import java.io.File;
025: import java.io.FileInputStream;
026: import java.io.FileOutputStream;
027: import java.io.IOException;
028: import java.io.InputStream;
029: import java.io.ObjectInputStream;
030: import java.io.ObjectOutputStream;
031: import java.util.ArrayList;
032: import java.util.HashMap;
033: import java.util.HashSet;
034: import java.util.Iterator;
035: import java.util.List;
036: import java.util.Stack;
037: import java.util.TreeSet;
038: import java.util.zip.ZipEntry;
039: import java.util.zip.ZipInputStream;
040: import java.util.zip.ZipOutputStream;
041:
042: import org.apache.regexp.RE;
043: import org.apache.regexp.RECompiler;
044: import org.apache.regexp.RESyntaxException;
045:
046: import com.izforge.izpack.LocaleDatabase;
047: import com.izforge.izpack.Pack;
048: import com.izforge.izpack.PackFile;
049: import com.izforge.izpack.UpdateCheck;
050: import com.izforge.izpack.event.InstallerListener;
051: import com.izforge.izpack.rules.RulesEngine;
052: import com.izforge.izpack.util.AbstractUIProgressHandler;
053: import com.izforge.izpack.util.Debug;
054: import com.izforge.izpack.util.IoHelper;
055: import com.izforge.izpack.util.VariableSubstitutor;
056:
057: /**
058: * Abstract base class for all unpacker implementations.
059: * @author Dennis Reil, <izpack@reil-online.de>
060: */
061: public abstract class UnpackerBase implements IUnpacker {
062: /** The installdata. */
063: protected AutomatedInstallData idata;
064:
065: /** The installer listener. */
066: protected AbstractUIProgressHandler handler;
067:
068: /** The uninstallation data. */
069: protected UninstallData udata;
070:
071: /** The variables substitutor. */
072: protected VariableSubstitutor vs;
073:
074: /** The absolute path of the installation. (NOT the canonical!) */
075: protected File absolute_installpath;
076:
077: /** The packs locale database. */
078: protected LocaleDatabase langpack = null;
079:
080: /** The result of the operation. */
081: protected boolean result = true;
082:
083: /** The instances of the unpacker objects. */
084: protected static HashMap<Object, String> instances = new HashMap<Object, String>();
085:
086: /** Interrupt flag if global interrupt is desired. */
087: protected static boolean interruptDesired = false;
088:
089: /** Do not perform a interrupt call. */
090: protected static boolean discardInterrupt = false;
091:
092: /** The name of the XML file that specifies the panel langpack */
093: protected static final String LANG_FILE_NAME = "packsLang.xml";
094:
095: public static final String ALIVE = "alive";
096:
097: public static final String INTERRUPT = "doInterrupt";
098:
099: public static final String INTERRUPTED = "interruppted";
100:
101: protected RulesEngine rules;
102:
103: /**
104: * The constructor.
105: *
106: * @param idata The installation data.
107: * @param handler The installation progress handler.
108: */
109: public UnpackerBase(AutomatedInstallData idata,
110: AbstractUIProgressHandler handler) {
111: try {
112: String resource = LANG_FILE_NAME + "_" + idata.localeISO3;
113: this .langpack = new LocaleDatabase(ResourceManager
114: .getInstance().getInputStream(resource));
115: } catch (Throwable exception) {
116: }
117:
118: this .idata = idata;
119: this .handler = handler;
120:
121: // Initialize the variable substitutor
122: vs = new VariableSubstitutor(idata.getVariables());
123: }
124:
125: public void setRules(RulesEngine rules) {
126: this .rules = rules;
127: }
128:
129: /**
130: * Returns a copy of the active unpacker instances.
131: *
132: * @return a copy of active unpacker instances
133: */
134: public static HashMap getRunningInstances() {
135: synchronized (instances) { // Return a shallow copy to prevent a
136: // ConcurrentModificationException.
137: return (HashMap) (instances.clone());
138: }
139: }
140:
141: /**
142: * Adds this to the map of all existent instances of Unpacker.
143: */
144: protected void addToInstances() {
145: synchronized (instances) {
146: instances.put(this , ALIVE);
147: }
148: }
149:
150: /**
151: * Removes this from the map of all existent instances of Unpacker.
152: */
153: protected void removeFromInstances() {
154: synchronized (instances) {
155: instances.remove(this );
156: }
157: }
158:
159: /**
160: * Initiate interrupt of all alive Unpacker. This method does not interrupt the Unpacker objects
161: * else it sets only the interrupt flag for the Unpacker objects. The dispatching of interrupt
162: * will be performed by the Unpacker objects self.
163: */
164: private static void setInterruptAll() {
165: synchronized (instances) {
166: Iterator iter = instances.keySet().iterator();
167: while (iter.hasNext()) {
168: Object key = iter.next();
169: if (instances.get(key).equals(ALIVE)) {
170: instances.put(key, INTERRUPT);
171: }
172: }
173: // Set global flag to allow detection of it in other classes.
174: // Do not set it to thread because an exec will then be stoped.
175: setInterruptDesired(true);
176: }
177: }
178:
179: /**
180: * Initiate interrupt of all alive Unpacker and waits until all Unpacker are interrupted or the
181: * wait time has arrived. If the doNotInterrupt flag in InstallerListener is set to true, the
182: * interrupt will be discarded.
183: *
184: * @param waitTime wait time in millisecounds
185: * @return true if the interrupt will be performed, false if the interrupt will be discarded
186: */
187: public static boolean interruptAll(long waitTime) {
188: long t0 = System.currentTimeMillis();
189: if (isDiscardInterrupt())
190: return (false);
191: setInterruptAll();
192: while (!isInterruptReady()) {
193: if (System.currentTimeMillis() - t0 > waitTime)
194: return (true);
195: try {
196: Thread.sleep(100);
197: } catch (InterruptedException e) {
198: }
199: }
200: return (true);
201: }
202:
203: private static boolean isInterruptReady() {
204: synchronized (instances) {
205: Iterator iter = instances.keySet().iterator();
206: while (iter.hasNext()) {
207: Object key = iter.next();
208: if (!instances.get(key).equals(INTERRUPTED))
209: return (false);
210: }
211: return (true);
212: }
213:
214: }
215:
216: /**
217: * Sets the interrupt flag for this Unpacker to INTERRUPTED if the previos state was INTERRUPT
218: * or INTERRUPTED and returns whether interrupt was initiate or not.
219: *
220: * @return whether interrupt was initiate or not
221: */
222: protected boolean performInterrupted() {
223: synchronized (instances) {
224: Object doIt = instances.get(this );
225: if (doIt != null
226: && (doIt.equals(INTERRUPT) || doIt
227: .equals(INTERRUPTED))) {
228: instances.put(this , INTERRUPTED);
229: this .result = false;
230: return (true);
231: }
232: return (false);
233: }
234: }
235:
236: /**
237: * Returns whether interrupt was initiate or not for this Unpacker.
238: *
239: * @return whether interrupt was initiate or not
240: */
241: private boolean shouldInterrupt() {
242: synchronized (instances) {
243: Object doIt = instances.get(this );
244: if (doIt != null
245: && (doIt.equals(INTERRUPT) || doIt
246: .equals(INTERRUPTED))) {
247: return (true);
248: }
249: return (false);
250: }
251:
252: }
253:
254: /**
255: * Return the state of the operation.
256: *
257: * @return true if the operation was successful, false otherwise.
258: */
259: public boolean getResult() {
260: return this .result;
261: }
262:
263: /**
264: * @param filename
265: * @param patterns
266: *
267: * @return true if the file matched one pattern, false if it did not
268: */
269: private boolean fileMatchesOnePattern(String filename,
270: ArrayList<RE> patterns) {
271: // first check whether any include matches
272: for (RE pattern : patterns) {
273: if (pattern.match(filename)) {
274: return true;
275: }
276: }
277:
278: return false;
279: }
280:
281: /**
282: * @param list A list of file name patterns (in ant fileset syntax)
283: * @param recompiler The regular expression compiler (used to speed up RE compiling).
284: *
285: * @return List of org.apache.regexp.RE
286: */
287: private List<RE> preparePatterns(ArrayList<String> list,
288: RECompiler recompiler) {
289: ArrayList<RE> result = new ArrayList<RE>();
290:
291: for (String element : list) {
292: if ((element != null) && (element.length() > 0)) {
293: // substitute variables in the pattern
294: element = this .vs.substitute(element, "plain");
295:
296: // check whether the pattern is absolute or relative
297: File f = new File(element);
298:
299: // if it is relative, make it absolute and prepend the
300: // installation path
301: // (this is a bit dangerous...)
302: if (!f.isAbsolute()) {
303: element = new File(this .absolute_installpath,
304: element).toString();
305: }
306:
307: // now parse the element and construct a regular expression from
308: // it
309: // (we have to parse it one character after the next because
310: // every
311: // character should only be processed once - it's not possible
312: // to get this
313: // correct using regular expression replacing)
314: StringBuffer element_re = new StringBuffer();
315:
316: int lookahead = -1;
317:
318: int pos = 0;
319:
320: while (pos < element.length()) {
321: char c;
322:
323: if (lookahead != -1) {
324: c = (char) lookahead;
325: lookahead = -1;
326: } else {
327: c = element.charAt(pos++);
328: }
329:
330: switch (c) {
331: case '/': {
332: element_re.append(File.separator);
333: break;
334: }
335: // escape backslash and dot
336: case '\\':
337: case '.': {
338: element_re.append("\\");
339: element_re.append(c);
340: break;
341: }
342: case '*': {
343: if (pos == element.length()) {
344: element_re.append("[^").append(
345: File.separator).append("]*");
346: break;
347: }
348:
349: lookahead = element.charAt(pos++);
350:
351: // check for "**"
352: if (lookahead == '*') {
353: element_re.append(".*");
354: // consume second star
355: lookahead = -1;
356: } else {
357: element_re.append("[^").append(
358: File.separator).append("]*");
359: // lookahead stays there
360: }
361: break;
362: }
363: default: {
364: element_re.append(c);
365: break;
366: }
367: } // switch
368:
369: }
370:
371: // make sure that the whole expression is matched
372: element_re.append('$');
373:
374: // replace \ by \\ and create a RE from the result
375: try {
376: result.add(new RE(recompiler.compile(element_re
377: .toString())));
378: } catch (RESyntaxException e) {
379: this .handler
380: .emitNotification("internal error: pattern \""
381: + element
382: + "\" produced invalid RE \""
383: + f.getPath() + "\"");
384: }
385:
386: }
387: }
388:
389: return result;
390: }
391:
392: // CUSTOM ACTION STUFF -------------- start -----------------
393:
394: /**
395: * Informs all listeners which would be informed at the given action type.
396: *
397: * @param customActions array of lists with the custom action objects
398: * @param action identifier for which callback should be called
399: * @param firstParam first parameter for the call
400: * @param secondParam second parameter for the call
401: * @param thirdParam third parameter for the call
402: */
403: protected void informListeners(List[] customActions, int action,
404: Object firstParam, Object secondParam, Object thirdParam)
405: throws Exception {
406: List listener = null;
407: // select the right action list.
408: switch (action) {
409: case InstallerListener.BEFORE_FILE:
410: case InstallerListener.AFTER_FILE:
411: case InstallerListener.BEFORE_DIR:
412: case InstallerListener.AFTER_DIR:
413: listener = customActions[customActions.length - 1];
414: break;
415: default:
416: listener = customActions[0];
417: break;
418: }
419: if (listener == null)
420: return;
421: // Iterate the action list.
422: Iterator iter = listener.iterator();
423: while (iter.hasNext()) {
424: if (shouldInterrupt())
425: return;
426: InstallerListener il = (InstallerListener) iter.next();
427: switch (action) {
428: case InstallerListener.BEFORE_FILE:
429: il
430: .beforeFile((File) firstParam,
431: (PackFile) secondParam);
432: break;
433: case InstallerListener.AFTER_FILE:
434: il.afterFile((File) firstParam, (PackFile) secondParam);
435: break;
436: case InstallerListener.BEFORE_DIR:
437: il.beforeDir((File) firstParam, (PackFile) secondParam);
438: break;
439: case InstallerListener.AFTER_DIR:
440: il.afterDir((File) firstParam, (PackFile) secondParam);
441: break;
442: case InstallerListener.BEFORE_PACK:
443: il.beforePack((Pack) firstParam, (Integer) secondParam,
444: (AbstractUIProgressHandler) thirdParam);
445: break;
446: case InstallerListener.AFTER_PACK:
447: il.afterPack((Pack) firstParam, (Integer) secondParam,
448: (AbstractUIProgressHandler) thirdParam);
449: break;
450: case InstallerListener.BEFORE_PACKS:
451: il.beforePacks((AutomatedInstallData) firstParam,
452: (Integer) secondParam,
453: (AbstractUIProgressHandler) thirdParam);
454: break;
455: case InstallerListener.AFTER_PACKS:
456: il.afterPacks((AutomatedInstallData) firstParam,
457: (AbstractUIProgressHandler) secondParam);
458: break;
459:
460: }
461: }
462: }
463:
464: /**
465: * Returns the defined custom actions split into types including a constructed type for the file
466: * related installer listeners.
467: *
468: * @return array of lists of custom action data like listeners
469: */
470: protected List[] getCustomActions() {
471: String[] listenerNames = AutomatedInstallData.CUSTOM_ACTION_TYPES;
472: List[] retval = new List[listenerNames.length + 1];
473: int i;
474: for (i = 0; i < listenerNames.length; ++i) {
475: retval[i] = idata.customData.get(listenerNames[i]);
476: if (retval[i] == null)
477: // Make a dummy list, then iterator is ever callable.
478: retval[i] = new ArrayList();
479: }
480: if (retval[AutomatedInstallData.INSTALLER_LISTENER_INDEX]
481: .size() > 0) { // Installer listeners exist
482: // Create file related installer listener list in the last
483: // element of custom action array.
484: i = retval.length - 1; // Should be so, but safe is safe ...
485: retval[i] = new ArrayList();
486: Iterator iter = retval[AutomatedInstallData.INSTALLER_LISTENER_INDEX]
487: .iterator();
488: while (iter.hasNext()) {
489: // If we get a class cast exception many is wrong and
490: // we must fix it.
491: InstallerListener li = (InstallerListener) iter.next();
492: if (li.isFileListener())
493: retval[i].add(li);
494: }
495:
496: }
497: return (retval);
498: }
499:
500: // This method is only used if a file related custom action exist.
501: /**
502: * Creates the given directory recursive and calls the method "afterDir" of each listener with
503: * the current file object and the pack file object. On error an exception is raised.
504: *
505: * @param dest the directory which should be created
506: * @param pf current pack file object
507: * @param customActions all defined custom actions
508: * @return false on error, true else
509: * @throws Exception
510: */
511:
512: protected boolean mkDirsWithEnhancement(File dest, PackFile pf,
513: List[] customActions) throws Exception {
514: String path = "unknown";
515: if (dest != null)
516: path = dest.getAbsolutePath();
517: if (dest != null && !dest.exists()
518: && dest.getParentFile() != null) {
519: if (dest.getParentFile().exists())
520: informListeners(customActions,
521: InstallerListener.BEFORE_DIR, dest, pf, null);
522: if (!dest.mkdir()) {
523: mkDirsWithEnhancement(dest.getParentFile(), pf,
524: customActions);
525: if (!dest.mkdir())
526: dest = null;
527: }
528: informListeners(customActions, InstallerListener.AFTER_DIR,
529: dest, pf, null);
530: }
531: if (dest == null) {
532: handler.emitError("Error creating directories",
533: "Could not create directory\n" + path);
534: handler.stopAction();
535: return (false);
536: }
537: return (true);
538: }
539:
540: // CUSTOM ACTION STUFF -------------- end -----------------
541:
542: /**
543: * Returns whether an interrupt request should be discarded or not.
544: *
545: * @return Returns the discard interrupt flag
546: */
547: public static synchronized boolean isDiscardInterrupt() {
548: return discardInterrupt;
549: }
550:
551: /**
552: * Sets the discard interrupt flag.
553: *
554: * @param di the discard interrupt flag to set
555: */
556: public static synchronized void setDiscardInterrupt(boolean di) {
557: discardInterrupt = di;
558: setInterruptDesired(false);
559: }
560:
561: /**
562: * Returns the interrupt desired state.
563: *
564: * @return the interrupt desired state
565: */
566: public static boolean isInterruptDesired() {
567: return interruptDesired;
568: }
569:
570: /**
571: * @param interruptDesired The interrupt desired flag to set
572: */
573: private static void setInterruptDesired(boolean interruptDesired) {
574: UnpackerBase.interruptDesired = interruptDesired;
575: }
576:
577: /**
578: * Puts the uninstaller.
579: *
580: * @exception Exception Description of the Exception
581: */
582: protected void putUninstaller() throws Exception {
583: // get the uninstaller base, returning if not found so that
584: // idata.uninstallOutJar remains null
585: InputStream[] in = new InputStream[2];
586: in[0] = UnpackerBase.class
587: .getResourceAsStream("/res/IzPack.uninstaller");
588: if (in[0] == null)
589: return;
590: // The uninstaller extension is facultative; it will be exist only
591: // if a native library was marked for uninstallation.
592: in[1] = UnpackerBase.class
593: .getResourceAsStream("/res/IzPack.uninstaller-ext");
594:
595: // Me make the .uninstaller directory
596: String dest = IoHelper.translatePath("$INSTALL_PATH", vs)
597: + File.separator + "Uninstaller";
598: String jar = dest + File.separator
599: + idata.info.getUninstallerName();
600: File pathMaker = new File(dest);
601: pathMaker.mkdirs();
602:
603: // We log the uninstaller deletion information
604: udata.setUninstallerJarFilename(jar);
605: udata.setUninstallerPath(dest);
606:
607: // We open our final jar file
608: FileOutputStream out = new FileOutputStream(jar);
609: // Intersect a buffer else byte for byte will be written to the file.
610: BufferedOutputStream bos = new BufferedOutputStream(out);
611: ZipOutputStream outJar = new ZipOutputStream(bos);
612: idata.uninstallOutJar = outJar;
613: outJar.setLevel(9);
614: udata.addFile(jar, true);
615:
616: // We copy the uninstallers
617: HashSet<String> doubles = new HashSet<String>();
618:
619: for (InputStream anIn : in) {
620: if (anIn == null) {
621: continue;
622: }
623: ZipInputStream inRes = new ZipInputStream(anIn);
624: ZipEntry zentry = inRes.getNextEntry();
625: while (zentry != null) {
626: // Puts a new entry, but not twice like META-INF
627: if (!doubles.contains(zentry.getName())) {
628: doubles.add(zentry.getName());
629: outJar.putNextEntry(new ZipEntry(zentry.getName()));
630:
631: // Byte to byte copy
632: int unc = inRes.read();
633: while (unc != -1) {
634: outJar.write(unc);
635: unc = inRes.read();
636: }
637:
638: // Next one please
639: inRes.closeEntry();
640: outJar.closeEntry();
641: }
642: zentry = inRes.getNextEntry();
643: }
644: inRes.close();
645: }
646:
647: // We put the langpack
648: InputStream in2 = Unpacker.class
649: .getResourceAsStream("/langpacks/" + idata.localeISO3
650: + ".xml");
651: outJar.putNextEntry(new ZipEntry("langpack.xml"));
652: int read = in2.read();
653: while (read != -1) {
654: outJar.write(read);
655: read = in2.read();
656: }
657: outJar.closeEntry();
658: }
659:
660: /**
661: * Adds additional unistall data to the uninstall data object.
662: *
663: * @param udata unistall data
664: * @param customData array of lists of custom action data like uninstaller listeners
665: */
666: protected void handleAdditionalUninstallData(UninstallData udata,
667: List[] customData) {
668: // Handle uninstall libs
669: udata
670: .addAdditionalData(
671: "__uninstallLibs__",
672: customData[AutomatedInstallData.UNINSTALLER_LIBS_INDEX]);
673: // Handle uninstaller listeners
674: udata
675: .addAdditionalData(
676: "uninstallerListeners",
677: customData[AutomatedInstallData.UNINSTALLER_LISTENER_INDEX]);
678: // Handle uninstaller jars
679: udata
680: .addAdditionalData(
681: "uninstallerJars",
682: customData[AutomatedInstallData.UNINSTALLER_JARS_INDEX]);
683: }
684:
685: public abstract void run();
686:
687: /**
688: * @param updatechecks
689: */
690: protected void performUpdateChecks(
691: ArrayList<UpdateCheck> updatechecks) {
692: ArrayList<RE> include_patterns = new ArrayList<RE>();
693: ArrayList<RE> exclude_patterns = new ArrayList<RE>();
694:
695: RECompiler recompiler = new RECompiler();
696:
697: this .absolute_installpath = new File(idata.getInstallPath())
698: .getAbsoluteFile();
699:
700: // at first, collect all patterns
701: for (UpdateCheck uc : updatechecks) {
702: if (uc.includesList != null) {
703: include_patterns.addAll(preparePatterns(
704: uc.includesList, recompiler));
705: }
706:
707: if (uc.excludesList != null) {
708: exclude_patterns.addAll(preparePatterns(
709: uc.excludesList, recompiler));
710: }
711: }
712:
713: // do nothing if no update checks were specified
714: if (include_patterns.size() == 0)
715: return;
716:
717: // now collect all files in the installation directory and figure
718: // out files to check for deletion
719:
720: // use a treeset for fast access
721: TreeSet<String> installed_files = new TreeSet<String>();
722:
723: for (String fname : this .udata.getInstalledFilesList()) {
724: File f = new File(fname);
725:
726: if (!f.isAbsolute()) {
727: f = new File(this .absolute_installpath, fname);
728: }
729:
730: installed_files.add(f.getAbsolutePath());
731: }
732:
733: // now scan installation directory (breadth first), contains Files of
734: // directories to scan
735: // (note: we'll recurse infinitely if there are circular links or
736: // similar nasty things)
737: Stack<File> scanstack = new Stack<File>();
738:
739: // contains File objects determined for deletion
740: ArrayList<File> files_to_delete = new ArrayList<File>();
741:
742: try {
743: scanstack.add(absolute_installpath);
744:
745: while (!scanstack.empty()) {
746: File f = scanstack.pop();
747:
748: File[] files = f.listFiles();
749:
750: if (files == null) {
751: throw new IOException(f.getPath()
752: + "is not a directory!");
753: }
754:
755: for (File newf : files) {
756: String newfname = newf.getPath();
757:
758: // skip files we just installed
759: if (installed_files.contains(newfname)) {
760: continue;
761: }
762:
763: if (fileMatchesOnePattern(newfname,
764: include_patterns)
765: && (!fileMatchesOnePattern(newfname,
766: exclude_patterns))) {
767: files_to_delete.add(newf);
768: }
769:
770: if (newf.isDirectory()) {
771: scanstack.push(newf);
772: }
773:
774: }
775: }
776: } catch (IOException e) {
777: this .handler.emitError(
778: "error while performing update checks", e
779: .toString());
780: }
781:
782: for (File f : files_to_delete) {
783: if (!f.isDirectory())
784: // skip directories - they cannot be removed safely yet
785: {
786: // this.handler.emitNotification("deleting " + f.getPath());
787: f.delete();
788: }
789:
790: }
791: }
792:
793: /**
794: * Writes information about the installed packs and the variables at
795: * installation time.
796: * @throws IOException
797: * @throws ClassNotFoundException
798: */
799: public void writeInstallationInformation() throws IOException,
800: ClassNotFoundException {
801: if (!idata.info.isWriteInstallationInformation()) {
802: Debug.trace("skip writing installation information");
803: return;
804: }
805: Debug.trace("writing installation information");
806: String installdir = idata.getInstallPath();
807:
808: List installedpacks = new ArrayList(idata.selectedPacks);
809:
810: File installationinfo = new File(installdir + File.separator
811: + AutomatedInstallData.INSTALLATION_INFORMATION);
812: if (!installationinfo.exists()) {
813: Debug.trace("creating info file"
814: + installationinfo.getAbsolutePath());
815: installationinfo.createNewFile();
816: } else {
817: Debug.trace("installation information found");
818: // read in old information and update
819: FileInputStream fin = new FileInputStream(installationinfo);
820: ObjectInputStream oin = new ObjectInputStream(fin);
821:
822: List packs = (List) oin.readObject();
823: for (Object pack1 : packs) {
824: Pack pack = (Pack) pack1;
825: installedpacks.add(pack);
826: }
827: oin.close();
828: fin.close();
829:
830: }
831:
832: FileOutputStream fout = new FileOutputStream(installationinfo);
833: ObjectOutputStream oout = new ObjectOutputStream(fout);
834: oout.writeObject(installedpacks);
835: /*
836: int selectedpackscount = idata.selectedPacks.size();
837: for (int i = 0; i < selectedpackscount; i++)
838: {
839: Pack pack = (Pack) idata.selectedPacks.get(i);
840: oout.writeObject(pack);
841: }
842: */
843: oout.writeObject(idata.variables);
844: Debug.trace("done.");
845: oout.close();
846: fout.close();
847: }
848: }
|