001: /*
002: * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
003: *
004: * http://izpack.org/ http://izpack.codehaus.org/
005: *
006: * Copyright 2007 Dennis Reil
007: *
008: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
009: * in compliance with the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software distributed under the License
014: * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
015: * or implied. See the License for the specific language governing permissions and limitations under
016: * the License.
017: */
018: package com.izforge.izpack.installer;
019:
020: import java.awt.Component;
021: import java.io.EOFException;
022: import java.io.File;
023: import java.io.FileInputStream;
024: import java.io.FileOutputStream;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.io.ObjectInputStream;
028: import java.io.PrintWriter;
029: import java.util.ArrayList;
030: import java.util.Enumeration;
031: import java.util.List;
032: import java.util.Properties;
033:
034: import javax.swing.JOptionPane;
035:
036: import com.izforge.izpack.ExecutableFile;
037: import com.izforge.izpack.Pack;
038: import com.izforge.izpack.PackFile;
039: import com.izforge.izpack.ParsableFile;
040: import com.izforge.izpack.UpdateCheck;
041: import com.izforge.izpack.XPackFile;
042: import com.izforge.izpack.event.InstallerListener;
043: import com.izforge.izpack.io.CorruptVolumeException;
044: import com.izforge.izpack.io.FileSpanningInputStream;
045: import com.izforge.izpack.io.FileSpanningOutputStream;
046: import com.izforge.izpack.io.VolumeNotFoundException;
047: import com.izforge.izpack.panels.NextMediaDialog;
048: import com.izforge.izpack.util.AbstractUIHandler;
049: import com.izforge.izpack.util.AbstractUIProgressHandler;
050: import com.izforge.izpack.util.Debug;
051: import com.izforge.izpack.util.FileExecutor;
052: import com.izforge.izpack.util.IoHelper;
053: import com.izforge.izpack.util.OsConstraint;
054:
055: /**
056: * Unpacker class for a multi volume installation.
057: *
058: * @author Dennis Reil, <izpack@reil-online.de>
059: */
060: public class MultiVolumeUnpacker extends UnpackerBase {
061: public MultiVolumeUnpacker(AutomatedInstallData idata,
062: AbstractUIProgressHandler handler) {
063: super (idata, handler);
064: }
065:
066: protected File enterNextMediaMessage(String volumename,
067: boolean lastcorrupt) {
068: if (lastcorrupt) {
069: Component parent = null;
070: if ((this .handler != null)
071: && (this .handler instanceof IzPanel)) {
072: parent = ((IzPanel) this .handler).getInstallerFrame();
073: }
074: JOptionPane.showMessageDialog(parent, idata.langpack
075: .getString("nextmedia.corruptmedia"),
076: idata.langpack
077: .getString("nextmedia.corruptmedia.title"),
078: JOptionPane.ERROR_MESSAGE);
079: }
080: Debug.trace("Enter next media: " + volumename);
081:
082: File nextvolume = new File(volumename);
083: NextMediaDialog nmd = null;
084:
085: while (!nextvolume.exists() || lastcorrupt) {
086: if ((this .handler != null)
087: && (this .handler instanceof IzPanel)) {
088: InstallerFrame installframe = ((IzPanel) this .handler)
089: .getInstallerFrame();
090: nmd = new NextMediaDialog(installframe, idata,
091: volumename);
092: } else {
093: nmd = new NextMediaDialog(null, idata, volumename);
094: }
095: nmd.setVisible(true);
096: String nextmediainput = nmd.getNextMedia();
097: if (nextmediainput != null) {
098: nextvolume = new File(nextmediainput);
099: } else {
100: Debug.trace("Input from NextMediaDialog was null");
101: nextvolume = new File(volumename);
102: }
103: // selection equal to last selected which was corrupt?
104: if (!(volumename.equals(nextvolume.getAbsolutePath()) && lastcorrupt)) {
105: lastcorrupt = false;
106: }
107: }
108: return nextvolume;
109: }
110:
111: protected File enterNextMediaMessage(String volumename) {
112: return enterNextMediaMessage(volumename, false);
113: }
114:
115: /** The run method. */
116: public void run() {
117: addToInstances();
118: try {
119: //
120: // Initialisations
121: FileOutputStream out = null;
122: ArrayList<ParsableFile> parsables = new ArrayList<ParsableFile>();
123: ArrayList<ExecutableFile> executables = new ArrayList<ExecutableFile>();
124: ArrayList<UpdateCheck> updatechecks = new ArrayList<UpdateCheck>();
125: List packs = idata.selectedPacks;
126: int npacks = packs.size();
127: Debug.trace("Unpacker starting");
128: handler.startAction("Unpacking", npacks);
129: udata = UninstallData.getInstance();
130: // Custom action listener stuff --- load listeners ----
131: List[] customActions = getCustomActions();
132: // Custom action listener stuff --- beforePacks ----
133: informListeners(customActions,
134: InstallerListener.BEFORE_PACKS, idata, npacks,
135: handler);
136: // vs = new VariableSubstitutor(idata.getVariables());
137: packs = idata.selectedPacks;
138: npacks = packs.size();
139: if (npacks == 0) {
140: if (performInterrupted()) { // Interrupt was initiated; perform it.
141: return;
142: }
143:
144: // Custom action listener stuff --- afterPacks ----
145: informListeners(customActions,
146: InstallerListener.AFTER_PACKS, idata, handler,
147: null);
148: if (performInterrupted()) { // Interrupt was initiated; perform it.
149: return;
150: }
151:
152: // The end :-)
153: handler.stopAction();
154: return;
155: }
156: InputStream in = MultiVolumeUnpacker.class
157: .getResourceAsStream(FileSpanningOutputStream.VOLUMES_INFO);
158: // get volumes metadata
159: ObjectInputStream metadataobj = new ObjectInputStream(in);
160: // TODO: create MetadataObject
161: int volumes = metadataobj.readInt();
162: String volumename = metadataobj.readUTF();
163: Debug.trace("Reading from " + volumes
164: + " volumes with basename " + volumename + " ");
165: metadataobj.close();
166: String mediadirectory = MultiVolumeInstaller
167: .getMediadirectory();
168: if ((mediadirectory == null)
169: || (mediadirectory.length() <= 0)) {
170: Debug.trace("Mediadirectory wasn't set.");
171: mediadirectory = System.getProperty("java.io.tmpdir"); // try the temporary
172: // directory
173: }
174: Debug.trace("Using mediadirectory = " + mediadirectory);
175: File volume = new File(mediadirectory + File.separator
176: + volumename);
177: if (!volume.exists()) {
178: volume = enterNextMediaMessage(volume.getAbsolutePath());
179: }
180: FileSpanningInputStream fin = new FileSpanningInputStream(
181: volume, volumes);
182:
183: // We unpack the selected packs
184: for (int i = 0; i < npacks; i++) {
185: // We get the pack stream
186: int n = idata.allPacks.indexOf(packs.get(i));
187:
188: in = MultiVolumeUnpacker.class
189: .getResourceAsStream("/packs/pack" + n);
190:
191: // Custom action listener stuff --- beforePack ----
192: informListeners(customActions,
193: InstallerListener.BEFORE_PACK, packs.get(i),
194: npacks, handler);
195: // find next Entry
196: ObjectInputStream objIn = new ObjectInputStream(in);
197: // We unpack the files
198: int nfiles = objIn.readInt();
199:
200: // We get the internationalized name of the pack
201: final Pack pack = ((Pack) packs.get(i));
202: // evaluate condition
203: if (pack.hasCondition() && (rules != null)) {
204: if (!rules.isConditionTrue(pack.getCondition())) {
205: // skip pack, condition is not fullfilled.
206: continue;
207: }
208: }
209: String stepname = pack.name;// the message to be passed to the
210: // installpanel
211: if (langpack != null
212: && !(pack.id == null || "".equals(pack.id))) {
213:
214: final String name = langpack.getString(pack.id);
215: if (name != null && !"".equals(name)) {
216: stepname = name;
217: }
218: }
219: handler.nextStep(stepname, i + 1, nfiles);
220: for (int j = 0; j < nfiles; j++) {
221: // We read the header
222: XPackFile pf = (XPackFile) objIn.readObject();
223: if (pf.hasCondition() && (rules != null)) {
224: if (!rules.isConditionTrue(pf.getCondition())) {
225: // skip file, condition is false
226: continue;
227: }
228: }
229: if (OsConstraint.oneMatchesCurrentSystem(pf
230: .osConstraints())) {
231: // We translate & build the path
232: String path = IoHelper.translatePath(pf
233: .getTargetPath(), vs);
234: File pathFile = new File(path);
235: File dest = pathFile;
236: if (!pf.isDirectory())
237: dest = pathFile.getParentFile();
238:
239: if (!dest.exists()) {
240: // If there are custom actions which would be called
241: // at
242: // creating a directory, create it recursively.
243: List fileListeners = customActions[customActions.length - 1];
244: if (fileListeners != null
245: && fileListeners.size() > 0)
246: mkDirsWithEnhancement(dest, pf,
247: customActions);
248: else
249: // Create it in on step.
250: {
251: if (!dest.mkdirs()) {
252: handler
253: .emitError(
254: "Error creating directories",
255: "Could not create directory\n"
256: + dest
257: .getPath());
258: handler.stopAction();
259: this .result = false;
260: return;
261: }
262: }
263: }
264:
265: if (pf.isDirectory())
266: continue;
267:
268: // Custom action listener stuff --- beforeFile ----
269: informListeners(customActions,
270: InstallerListener.BEFORE_FILE,
271: pathFile, pf, null);
272: // We add the path to the log,
273: udata.addFile(path, pack.uninstall);
274:
275: handler.progress(j, path);
276:
277: // if this file exists and should not be overwritten,
278: // check
279: // what to do
280: if ((pathFile.exists())
281: && (pf.override() != PackFile.OVERRIDE_TRUE)) {
282: boolean overwritefile = false;
283:
284: // don't overwrite file if the user said so
285: if (pf.override() != PackFile.OVERRIDE_FALSE) {
286: if (pf.override() == PackFile.OVERRIDE_TRUE) {
287: overwritefile = true;
288: } else if (pf.override() == PackFile.OVERRIDE_UPDATE) {
289: // check mtime of involved files
290: // (this is not 100% perfect, because the
291: // already existing file might
292: // still be modified but the new installed
293: // is just a bit newer; we would
294: // need the creation time of the existing
295: // file or record with which mtime
296: // it was installed...)
297: overwritefile = (pathFile
298: .lastModified() < pf
299: .lastModified());
300: } else {
301: int def_choice = -1;
302:
303: if (pf.override() == PackFile.OVERRIDE_ASK_FALSE)
304: def_choice = AbstractUIHandler.ANSWER_NO;
305: if (pf.override() == PackFile.OVERRIDE_ASK_TRUE)
306: def_choice = AbstractUIHandler.ANSWER_YES;
307:
308: int answer = handler
309: .askQuestion(
310: idata.langpack
311: .getString("InstallPanel.overwrite.title")
312: + " - "
313: + pathFile
314: .getName(),
315: idata.langpack
316: .getString("InstallPanel.overwrite.question")
317: + pathFile
318: .getAbsolutePath(),
319: AbstractUIHandler.CHOICES_YES_NO,
320: def_choice);
321:
322: overwritefile = (answer == AbstractUIHandler.ANSWER_YES);
323: }
324:
325: }
326:
327: if (!overwritefile) {
328: if (!pf.isBackReference()
329: && !((Pack) packs.get(i)).loose) {
330: // objIn.skip(pf.length());
331: }
332: continue;
333: }
334:
335: }
336:
337: // We copy the file
338: out = new FileOutputStream(pathFile);
339: byte[] buffer = new byte[5120];
340: long bytesCopied = 0;
341: // InputStream pis = objIn;
342: InputStream pis = fin;
343:
344: if (((Pack) packs.get(i)).loose) {
345: pis = new FileInputStream(pf.sourcePath);
346: }
347:
348: // read in the position of this file
349: // long fileposition = objIn.readLong();
350: long fileposition = pf.getArchivefileposition();
351:
352: while (fin.getFilepointer() < fileposition) {
353: // we have to skip some bytes
354: Debug
355: .trace("Skipping bytes to get to file "
356: + pathFile.getName()
357: + " ("
358: + fin.getFilepointer()
359: + "<"
360: + fileposition
361: + ") target is: "
362: + (fileposition - fin
363: .getFilepointer()));
364: try {
365: fin.skip(fileposition
366: - fin.getFilepointer());
367: break;
368: } catch (VolumeNotFoundException vnfe) {
369: File nextmedia = enterNextMediaMessage(vnfe
370: .getVolumename());
371: fin.setVolumename(nextmedia
372: .getAbsolutePath());
373: } catch (CorruptVolumeException cve) {
374: Debug
375: .trace("corrupt media found. magic number is not correct");
376: File nextmedia = enterNextMediaMessage(
377: cve.getVolumename(), true);
378: fin.setVolumename(nextmedia
379: .getAbsolutePath());
380: }
381: }
382:
383: if (fin.getFilepointer() > fileposition) {
384: Debug
385: .trace("Error, can't access file in pack.");
386: }
387:
388: while (bytesCopied < pf.length()) {
389: try {
390: if (performInterrupted()) { // Interrupt was initiated; perform it.
391: out.close();
392: if (pis != objIn)
393: pis.close();
394: return;
395: }
396: int maxBytes = (int) Math.min(pf
397: .length()
398: - bytesCopied, buffer.length);
399:
400: int bytesInBuffer = pis.read(buffer, 0,
401: maxBytes);
402: if (bytesInBuffer == -1) {
403: Debug
404: .trace("Unexpected end of stream (installer corrupted?)");
405: throw new IOException(
406: "Unexpected end of stream (installer corrupted?)");
407: }
408:
409: out.write(buffer, 0, bytesInBuffer);
410:
411: bytesCopied += bytesInBuffer;
412: } catch (VolumeNotFoundException vnfe) {
413: File nextmedia = enterNextMediaMessage(vnfe
414: .getVolumename());
415: fin.setVolumename(nextmedia
416: .getAbsolutePath());
417: } catch (CorruptVolumeException cve) {
418: Debug
419: .trace("corrupt media found. magic number is not correct");
420: File nextmedia = enterNextMediaMessage(
421: cve.getVolumename(), true);
422: fin.setVolumename(nextmedia
423: .getAbsolutePath());
424: }
425: }
426: // Cleanings
427: out.close();
428: // if (pis != objIn) pis.close();
429:
430: // Set file modification time if specified
431: if (pf.lastModified() >= 0)
432: pathFile.setLastModified(pf.lastModified());
433: // Custom action listener stuff --- afterFile ----
434: informListeners(customActions,
435: InstallerListener.AFTER_FILE, pathFile,
436: pf, null);
437: } else {
438: if (!pf.isBackReference()) {
439: // objIn.skip(pf.length());
440: }
441: }
442: }
443: // Load information about parsable files
444: int numParsables = objIn.readInt();
445: Debug.trace("Looking for parsables");
446: for (int k = 0; k < numParsables; k++) {
447: ParsableFile pf = null;
448: while (true) {
449: try {
450: pf = (ParsableFile) objIn.readObject();
451: break;
452: } catch (VolumeNotFoundException vnfe) {
453: File nextmedia = enterNextMediaMessage(vnfe
454: .getVolumename());
455: fin.setVolumename(nextmedia
456: .getAbsolutePath());
457: } catch (CorruptVolumeException cve) {
458: Debug
459: .trace("corrupt media found. magic number is not correct");
460: File nextmedia = enterNextMediaMessage(cve
461: .getVolumename(), true);
462: fin.setVolumename(nextmedia
463: .getAbsolutePath());
464: } catch (EOFException eofe) {
465: File nextmedia = enterNextMediaMessage("");
466: fin.setVolumename(nextmedia
467: .getAbsolutePath());
468: }
469: }
470: if (pf.hasCondition() && (rules != null)) {
471: if (!rules.isConditionTrue(pf.getCondition())) {
472: // skip parsable, condition is false
473: continue;
474: }
475: }
476: pf.path = IoHelper.translatePath(pf.path, vs);
477: Debug.trace("Found parsable: " + pf.path);
478: parsables.add(pf);
479: }
480:
481: // Load information about executable files
482: int numExecutables = objIn.readInt();
483: Debug.trace("Looking for executables...");
484: for (int k = 0; k < numExecutables; k++) {
485: ExecutableFile ef = (ExecutableFile) objIn
486: .readObject();
487: if (ef.hasCondition() && (rules != null)) {
488: if (!rules.isConditionTrue(ef.getCondition())) {
489: // skip, condition is false
490: continue;
491: }
492: }
493: ef.path = IoHelper.translatePath(ef.path, vs);
494: if (null != ef.argList && !ef.argList.isEmpty()) {
495: String arg = null;
496: for (int j = 0; j < ef.argList.size(); j++) {
497: arg = ef.argList.get(j);
498: arg = IoHelper.translatePath(arg, vs);
499: ef.argList.set(j, arg);
500: }
501: }
502: Debug.trace("Found executable: " + ef.path);
503: executables.add(ef);
504: if (ef.executionStage == ExecutableFile.UNINSTALL) {
505: udata.addExecutable(ef);
506: }
507: }
508: // Custom action listener stuff --- uninstall data ----
509: handleAdditionalUninstallData(udata, customActions);
510:
511: // Load information about updatechecks
512: int numUpdateChecks = objIn.readInt();
513: Debug.trace("Looking for updatechecks");
514: for (int k = 0; k < numUpdateChecks; k++) {
515: UpdateCheck uc = (UpdateCheck) objIn.readObject();
516: Debug.trace("found updatecheck");
517: updatechecks.add(uc);
518: }
519:
520: // objIn.close();
521:
522: if (performInterrupted()) { // Interrupt was initiated; perform it.
523: return;
524: }
525:
526: // Custom action listener stuff --- afterPack ----
527: informListeners(customActions,
528: InstallerListener.AFTER_PACK, packs.get(i), i,
529: handler);
530: }
531: Debug.trace("Trying to parse files");
532: // We use the scripts parser
533: ScriptParser parser = new ScriptParser(parsables, vs);
534: parser.parseFiles();
535: Debug.trace("parsed files");
536: if (performInterrupted()) { // Interrupt was initiated; perform it.
537: return;
538: }
539: Debug.trace("Trying to execute files");
540: // We use the file executor
541: FileExecutor executor = new FileExecutor(executables);
542: if (executor.executeFiles(ExecutableFile.POSTINSTALL,
543: handler) != 0) {
544: handler.emitError("File execution failed",
545: "The installation was not completed");
546: this .result = false;
547: Debug.trace("File execution failed");
548: }
549:
550: if (performInterrupted()) { // Interrupt was initiated; perform it.
551: return;
552: }
553: Debug.trace("Create uninstaller");
554: // We put the uninstaller (it's not yet complete...)
555: putUninstaller();
556: Debug.trace("Uninstaller created");
557: // update checks _after_ uninstaller was put, so we don't delete it
558: Debug.trace("Perform updateChecks");
559: performUpdateChecks(updatechecks);
560: Debug.trace("updatechecks performed.");
561: if (performInterrupted()) { // Interrupt was initiated; perform it.
562: return;
563: }
564:
565: // Custom action listener stuff --- afterPacks ----
566: informListeners(customActions,
567: InstallerListener.AFTER_PACKS, idata, handler, null);
568: if (performInterrupted()) { // Interrupt was initiated; perform it.
569: return;
570: }
571:
572: // write installation information
573: writeInstallationInformation();
574:
575: this .writeConfigInformation();
576: // The end :-)
577: handler.stopAction();
578: Debug.trace("Installation complete");
579: } catch (Exception err) {
580: // TODO: finer grained error handling with useful error messages
581: handler.stopAction();
582: handler.emitError("An error occured", err.toString());
583: err.printStackTrace();
584: Debug.trace("Error while installing: " + err.toString());
585: this .result = false;
586: } finally {
587: removeFromInstances();
588: }
589: }
590:
591: protected void writeConfigInformation() {
592: // save the variables
593: Properties installerproperties = idata.getVariables();
594: Enumeration installerpropertieskeys = installerproperties
595: .keys();
596: try {
597: String installpath = idata.getVariable("INSTALL_PATH");
598: PrintWriter pw = new PrintWriter(new FileOutputStream(
599: installpath + File.separator
600: + "installer.properties"));
601: pw
602: .println("# Installer properties, written by MultiVolumeUnpacker.");
603: while (installerpropertieskeys.hasMoreElements()) {
604: String key = (String) installerpropertieskeys
605: .nextElement();
606: if (key.startsWith("SYSTEM_")) {
607: // skip
608: continue;
609: } else if (key.startsWith("password_")) {
610: // skip
611: continue;
612: }
613: pw.println(key + "="
614: + installerproperties.getProperty(key));
615: }
616: pw.flush();
617: pw.close();
618: } catch (Exception e) {
619: Debug
620: .trace("Error while writing config information in MultiVolumeUnpacker: "
621: + e.getMessage());
622: }
623: }
624: }
|