1 18 package com.izforge.izpack.compiler; 19 20 import java.io.BufferedReader ; 21 import java.io.File ; 22 import java.io.FileInputStream ; 23 import java.io.FileOutputStream ; 24 import java.io.IOException ; 25 import java.io.InputStream ; 26 import java.io.ObjectOutputStream ; 27 import java.io.OutputStream ; 28 import java.io.StringReader ; 29 import java.net.URL ; 30 import java.util.ArrayList ; 31 import java.util.HashMap ; 32 import java.util.HashSet ; 33 import java.util.Iterator ; 34 import java.util.List ; 35 import java.util.Map ; 36 import java.util.Properties ; 37 import java.util.Set ; 38 import java.util.zip.Deflater ; 39 import java.util.zip.ZipEntry ; 40 import java.util.zip.ZipException ; 41 import java.util.zip.ZipFile ; 42 import java.util.zip.ZipInputStream ; 43 import java.util.zip.ZipOutputStream ; 44 45 import net.n3.nanoxml.XMLElement; 46 47 import com.izforge.izpack.CustomData; 48 import com.izforge.izpack.GUIPrefs; 49 import com.izforge.izpack.Info; 50 import com.izforge.izpack.Pack; 51 import com.izforge.izpack.PackFile; 52 import com.izforge.izpack.Panel; 53 import com.izforge.izpack.XPackFile; 54 import com.izforge.izpack.compressor.PackCompressor; 55 import com.izforge.izpack.compressor.PackCompressorFactory; 56 import com.izforge.izpack.io.FileSpanningInputStream; 57 import com.izforge.izpack.io.FileSpanningOutputStream; 58 import com.izforge.izpack.util.Debug; 59 60 68 public class MultiVolumePackager implements IPackager 69 { 70 71 public static final String INSTALLER_PAK_NAME = "installer"; 72 73 74 public static final String SKELETON_SUBPATH = "lib/installer.jar"; 75 76 77 private File baseFile = null; 78 79 80 private ZipOutputStream primaryJarStream; 81 82 83 private Info info = null; 84 85 86 private GUIPrefs guiPrefs = null; 87 88 89 private Properties variables = new Properties (); 90 91 92 private List panelList = new ArrayList (); 93 94 95 private List packsList = new ArrayList (); 96 97 98 private List langpackNameList = new ArrayList (); 99 100 101 private List customDataList = new ArrayList (); 102 103 104 private Map installerResourceURLMap = new HashMap (); 105 106 107 private Set includedJarURLs = new HashSet (); 108 109 110 private boolean packJarsSeparate = false; 111 112 113 private PackagerListener listener; 114 115 116 private PackCompressor compressor; 117 118 119 private HashMap alreadyWrittenFiles = new HashMap (); 120 121 private XMLElement configdata = null; 122 123 128 public MultiVolumePackager() throws CompilerException 129 { 130 this("default"); 131 } 132 133 139 public MultiVolumePackager(String compr_format) throws CompilerException 140 { 141 this(compr_format, -1); 142 } 143 144 152 public MultiVolumePackager(String compr_format, int compr_level) throws CompilerException 153 { 154 setCompressorOptions(compr_format, compr_level); 155 } 156 157 164 public void createInstaller(File primaryFile) throws Exception 165 { 166 this.analyzeConfigurationInformation(); 168 169 String baseName = primaryFile.getName(); 171 if (baseName.endsWith(".jar")) 172 { 173 baseName = baseName.substring(0, baseName.length() - 4); 174 baseFile = new File (primaryFile.getParentFile(), baseName); 175 } 176 else 177 baseFile = primaryFile; 178 179 info.setInstallerBase(baseFile.getName()); 180 packJarsSeparate = (info.getWebDirURL() != null); 181 182 primaryJarStream = getJarOutputStream(baseFile.getName() + ".jar"); 184 185 sendStart(); 186 187 System.out.println("Writing skeleton installer."); 191 writeSkeletonInstaller(); 192 writeInstallerObject("info", info); 193 writeInstallerObject("vars", variables); 194 writeInstallerObject("GUIPrefs", guiPrefs); 195 writeInstallerObject("panelsOrder", panelList); 196 writeInstallerObject("customData", customDataList); 197 writeInstallerObject("langpacks.info", langpackNameList); 198 writeInstallerResources(); 199 writeIncludedJars(); 200 201 String packfile = baseFile.getParent() + File.separator + INSTALLER_PAK_NAME; 203 writePacks(new File (packfile)); 204 205 primaryJarStream.close(); 212 213 sendStop(); 214 } 215 216 219 220 private void analyzeConfigurationInformation() 221 { 222 String classname = this.getClass().getName(); 223 String sizeprop = classname + ".volumesize"; 224 String freespaceprop = classname + ".firstvolumefreespace"; 225 if (this.configdata == null){ 226 this.variables.setProperty(sizeprop, Long.toString(FileSpanningOutputStream.DEFAULT_VOLUME_SIZE)); 228 this.variables.setProperty(freespaceprop, Long.toString(FileSpanningOutputStream.DEFAULT_ADDITIONAL_FIRST_VOLUME_FREE_SPACE_SIZE)); 229 } 230 else { 231 String volumesize = configdata.getAttribute("volumesize", Long.toString(FileSpanningOutputStream.DEFAULT_VOLUME_SIZE)); 233 String freespace = configdata.getAttribute("firstvolumefreespace", Long.toString(FileSpanningOutputStream.DEFAULT_ADDITIONAL_FIRST_VOLUME_FREE_SPACE_SIZE)); 234 this.variables.setProperty(sizeprop, volumesize); 235 this.variables.setProperty(freespaceprop, freespace); 236 } 237 } 238 239 244 public PackagerListener getPackagerListener() 245 { 246 return listener; 247 } 248 249 254 public void setPackagerListener(PackagerListener listener) 255 { 256 this.listener = listener; 257 } 258 259 264 private void sendMsg(String job) 265 { 266 sendMsg(job, PackagerListener.MSG_INFO); 267 } 268 269 275 private void sendMsg(String job, int priority) 276 { 277 Debug.trace(job); 278 if (listener != null) listener.packagerMsg(job, priority); 279 } 280 281 282 private void sendStart() 283 { 284 if (listener != null) listener.packagerStart(); 285 } 286 287 288 private void sendStop() 289 { 290 if (listener != null) listener.packagerStop(); 291 } 292 293 296 297 303 public void setInfo(Info info) throws Exception 304 { 305 sendMsg("Setting the installer information", PackagerListener.MSG_VERBOSE); 306 this.info = info; 307 if (!getCompressor().useStandardCompression() 308 && getCompressor().getDecoderMapperName() != null) 309 { 310 this.info.setPackDecoderClassName(getCompressor().getDecoderMapperName()); 311 } 312 } 313 314 319 public void setGUIPrefs(GUIPrefs prefs) 320 { 321 sendMsg("Setting the GUI preferences", PackagerListener.MSG_VERBOSE); 322 guiPrefs = prefs; 323 } 324 325 331 public Properties getVariables() 332 { 333 return variables; 334 } 335 336 340 public void addPanelJar(Panel panel, URL jarURL) 341 { 342 panelList.add(panel); addJarContent(jarURL); } 346 347 354 public void addCustomJar(CustomData ca, URL url) 355 { 356 customDataList.add(ca); addJarContent(url); } 360 361 366 public void addPack(PackInfo pack) 367 { 368 packsList.add(pack); 369 } 370 371 374 public List getPacksList() 375 { 376 return packsList; 377 } 378 379 386 public void addLangPack(String iso3, URL xmlURL, URL flagURL) 387 { 388 sendMsg("Adding langpack: " + iso3, PackagerListener.MSG_VERBOSE); 389 langpackNameList.add(iso3); 392 addResource("flag." + iso3, flagURL); 393 installerResourceURLMap.put("langpacks/" + iso3 + ".xml", xmlURL); 394 } 395 396 402 public void addResource(String resId, URL url) 403 { 404 sendMsg("Adding resource: " + resId, PackagerListener.MSG_VERBOSE); 405 installerResourceURLMap.put("res/" + resId, url); 406 } 407 408 415 public void addNativeLibrary(String name, URL url) throws Exception 416 { 417 sendMsg("Adding native library: " + name, PackagerListener.MSG_VERBOSE); 418 installerResourceURLMap.put("native/" + name, url); 419 } 420 421 428 public void addJarContent(URL jarURL) 429 { 430 addJarContent(jarURL, null); 431 } 432 433 440 public void addJarContent(URL jarURL, List files) 441 { 442 Object [] cont = { jarURL, files}; 443 sendMsg("Adding content of jar: " + jarURL.getFile(), PackagerListener.MSG_VERBOSE); 444 includedJarURLs.add(cont); 445 } 446 447 452 public void addNativeUninstallerLibrary(CustomData data) 453 { 454 customDataList.add(data); 457 } 458 459 462 463 467 private void writeSkeletonInstaller() throws IOException 468 { 469 sendMsg("Copying the skeleton installer", PackagerListener.MSG_VERBOSE); 470 471 472 InputStream is = MultiVolumePackager.class.getResourceAsStream("/" + SKELETON_SUBPATH); 473 if (is == null) 474 { 475 File skeleton = new File (Compiler.IZPACK_HOME, SKELETON_SUBPATH); 476 is = new FileInputStream (skeleton); 477 } 478 ZipInputStream inJarStream = new ZipInputStream (is); 479 480 List excludes = new ArrayList (); 482 excludes.add("META-INF.MANIFEST.MF"); 483 copyZipWithoutExcludes(inJarStream, primaryJarStream,excludes); 484 485 is = MultiVolumePackager.class.getResourceAsStream("/" + SKELETON_SUBPATH); 488 if (is == null) 489 { 490 File skeleton = new File (Compiler.IZPACK_HOME, SKELETON_SUBPATH); 491 is = new FileInputStream (skeleton); 492 } 493 inJarStream = new ZipInputStream (is); 494 boolean found = false; 495 ZipEntry ze = null; 496 String modifiedmanifest = null; 497 while (((ze = inJarStream.getNextEntry()) != null) && !found){ 498 if ("META-INF/MANIFEST.MF".equals(ze.getName())){ 499 long size = ze.getSize(); 500 byte[] buffer = new byte[4096]; 501 int readbytes = 0; 502 int totalreadbytes = 0; 503 StringBuffer manifest = new StringBuffer (); 504 while (((readbytes = inJarStream.read(buffer)) > 0) && (totalreadbytes < size)){ 505 totalreadbytes += readbytes; 506 String tmp = new String (buffer,0,readbytes,"utf-8"); 507 manifest.append(tmp); 508 } 509 510 511 StringReader stringreader = new StringReader (manifest.toString()); 512 BufferedReader reader = new BufferedReader (stringreader); 513 String line = null; 514 StringBuffer modified = new StringBuffer (); 515 while ((line = reader.readLine()) != null){ 516 if (line.startsWith("Main-Class:")){ 517 line = "Main-Class: com.izforge.izpack.installer.MultiVolumeInstaller"; 518 } 519 modified.append(line); 520 modified.append("\r\n"); 521 } 522 reader.close(); 523 modifiedmanifest = modified.toString(); 524 530 break; 531 } 532 } 533 534 primaryJarStream.putNextEntry(new ZipEntry ("META-INF/MANIFEST.MF")); 535 primaryJarStream.write(modifiedmanifest.getBytes()); 536 primaryJarStream.closeEntry(); 537 } 538 539 542 private void writeInstallerObject(String entryName, Object object) throws IOException 543 { 544 primaryJarStream.putNextEntry(new ZipEntry (entryName)); 545 ObjectOutputStream out = new ObjectOutputStream (primaryJarStream); 546 out.writeObject(object); 547 out.flush(); 548 primaryJarStream.closeEntry(); 549 } 550 551 552 private void writeInstallerResources() throws IOException 553 { 554 sendMsg("Copying " + installerResourceURLMap.size() + " files into installer"); 555 556 Iterator i = installerResourceURLMap.keySet().iterator(); 557 while (i.hasNext()) 558 { 559 String name = (String ) i.next(); 560 InputStream in = ((URL ) installerResourceURLMap.get(name)).openStream(); 561 primaryJarStream.putNextEntry(new ZipEntry (name)); 562 copyStream(in, primaryJarStream); 563 primaryJarStream.closeEntry(); 564 in.close(); 565 } 566 } 567 568 569 private void writeIncludedJars() throws IOException 570 { 571 sendMsg("Merging " + includedJarURLs.size() + " jars into installer"); 572 573 Iterator i = includedJarURLs.iterator(); 574 while (i.hasNext()) 575 { 576 Object [] current = (Object []) i.next(); 577 InputStream is = ((URL ) current[0]).openStream(); 578 ZipInputStream inJarStream = new ZipInputStream (is); 579 copyZip(inJarStream, primaryJarStream, (List ) current[1]); 580 } 581 } 582 583 586 private void writePacks(File primaryfile) throws Exception 587 { 588 589 final int num = packsList.size(); 590 sendMsg("Writing " + num + " Pack" + (num > 1 ? "s" : "") + " into installer"); 591 Debug.trace("Writing " + num + " Pack" + (num > 1 ? "s" : "") + " into installer"); 592 Map storedFiles = new HashMap (); 594 595 598 String classname = this.getClass().getName(); 599 String volumesize = this.getVariables().getProperty(classname + ".volumesize"); 600 String extraspace = this.getVariables().getProperty(classname + ".firstvolumefreespace"); 601 602 long volumesizel = FileSpanningOutputStream.DEFAULT_VOLUME_SIZE; 603 long extraspacel = FileSpanningOutputStream.DEFAULT_ADDITIONAL_FIRST_VOLUME_FREE_SPACE_SIZE; 604 605 if (volumesize != null) 606 { 607 volumesizel = Long.parseLong(volumesize); 608 } 609 if (extraspace != null) 610 { 611 extraspacel = Long.parseLong(extraspace); 612 } 613 Debug.trace("Volumesize: " + volumesizel); 614 Debug.trace("Extra space on first volume: " + extraspacel); 615 FileSpanningOutputStream fout = new FileSpanningOutputStream(primaryfile.getParent() 616 + File.separator + primaryfile.getName() + ".pak", volumesizel); 617 fout.setFirstvolumefreespacesize(extraspacel); 618 619 int packNumber = 0; 620 Iterator packIter = packsList.iterator(); 621 while (packIter.hasNext()) 622 { 623 PackInfo packInfo = (PackInfo) packIter.next(); 624 Pack pack = packInfo.getPack(); 625 pack.nbytes = 0; 626 627 sendMsg("Writing Pack " + packNumber + ": " + pack.name, PackagerListener.MSG_VERBOSE); 628 Debug.trace("Writing Pack " + packNumber + ": " + pack.name); 629 ZipEntry entry = new ZipEntry ("packs/pack" + packNumber); 630 637 primaryJarStream.putNextEntry(entry); 638 ObjectOutputStream objOut = new ObjectOutputStream (primaryJarStream); 639 640 objOut.writeInt(packInfo.getPackFiles().size()); 642 643 Iterator iter = packInfo.getPackFiles().iterator(); 644 while (iter.hasNext()) 645 { 646 boolean addFile = !pack.loose; 647 XPackFile pf = new XPackFile((PackFile) iter.next()); 648 File file = packInfo.getFile(pf.getPackfile()); 649 Debug.trace("Next file: " + file.getAbsolutePath()); 650 long[] info = (long[]) storedFiles.get(file); 653 if (info != null && !packJarsSeparate) 654 { 655 Debug.trace("File already included in other pack"); 656 pf.setPreviousPackFileRef((int) info[0], info[1]); 657 addFile = false; 658 } 659 660 if (addFile && !pf.isDirectory()) 661 { 662 long pos = fout.getFilepointer(); 663 664 pf.setArchivefileposition(pos); 665 666 int volumecountbeforewrite = fout.getVolumeCount(); 668 669 FileInputStream inStream = new FileInputStream (file); 670 long bytesWritten = copyStream(inStream, fout); 671 fout.flush(); 672 673 long posafterwrite = fout.getFilepointer(); 674 Debug.trace("File (" + pf.sourcePath + ") " + pos + " <-> " + posafterwrite); 675 676 if (fout.getFilepointer() != (pos + bytesWritten)) 677 { 678 Debug.trace("file: " + file.getName()); 679 Debug.trace("(Filepos/BytesWritten/ExpectedNewFilePos/NewFilePointer) (" 680 + pos + "/" + bytesWritten + "/" + (pos + bytesWritten) 681 + "/" + fout.getFilepointer() + ")"); 682 Debug.trace("Volumecount (before/after) (" 683 + volumecountbeforewrite + "/" + fout.getVolumeCount() + ")"); 684 throw new IOException ("Error new filepointer is illegal"); 685 } 686 687 if (bytesWritten != pf.length()) { throw new IOException ( 688 "File size mismatch when reading " + file); } 689 inStream.close(); 690 } 693 694 objOut.writeObject(pf); objOut.flush(); pack.nbytes += pf.length(); 698 } 699 objOut.writeInt(packInfo.getParsables().size()); 701 iter = packInfo.getParsables().iterator(); 702 while (iter.hasNext()) 703 objOut.writeObject(iter.next()); 704 705 objOut.writeInt(packInfo.getExecutables().size()); 707 iter = packInfo.getExecutables().iterator(); 708 while (iter.hasNext()) 709 objOut.writeObject(iter.next()); 710 711 objOut.writeInt(packInfo.getUpdateChecks().size()); 713 iter = packInfo.getUpdateChecks().iterator(); 714 while (iter.hasNext()) 715 objOut.writeObject(iter.next()); 716 717 objOut.flush(); 719 packNumber++; 720 } 721 722 int volumes = fout.getVolumeCount(); 724 Debug.trace("Written " + volumes + " volumes"); 725 String volumename = primaryfile.getName() + ".pak"; 726 727 fout.flush(); 728 fout.close(); 729 730 primaryJarStream.putNextEntry(new ZipEntry ("volumes.info")); 731 ObjectOutputStream out = new ObjectOutputStream (primaryJarStream); 732 out.writeInt(volumes); 733 out.writeUTF(volumename); 734 out.flush(); 735 primaryJarStream.closeEntry(); 736 737 primaryJarStream.putNextEntry(new ZipEntry ("packs.info")); 739 out = new ObjectOutputStream (primaryJarStream); 740 out.writeInt(packsList.size()); 741 742 Iterator i = packsList.iterator(); 743 while (i.hasNext()) 744 { 745 PackInfo pack = (PackInfo) i.next(); 746 out.writeObject(pack.getPack()); 747 } 748 out.flush(); 749 primaryJarStream.closeEntry(); 750 } 751 752 755 756 757 private ZipOutputStream getJarOutputStream(String name) throws IOException 758 { 759 File file = new File (baseFile.getParentFile(), name); 760 sendMsg("Building installer jar: " + file.getAbsolutePath()); 761 Debug.trace("Building installer jar: " + file.getAbsolutePath()); 762 ZipOutputStream jar = new ZipOutputStream (new FileOutputStream (file)); 763 jar.setLevel(Deflater.BEST_COMPRESSION); 764 768 return jar; 769 } 770 771 780 private void copyZip(ZipInputStream zin, ZipOutputStream out) throws IOException 781 { 782 copyZip(zin, out, null); 783 } 784 785 794 private void copyZip(ZipInputStream zin, ZipOutputStream out, List files) throws IOException 795 { 796 java.util.zip.ZipEntry zentry; 797 if (!alreadyWrittenFiles.containsKey(out)) alreadyWrittenFiles.put(out, new HashSet ()); 798 HashSet currentSet = (HashSet ) alreadyWrittenFiles.get(out); 799 while ((zentry = zin.getNextEntry()) != null) 800 { 801 String currentName = zentry.getName(); 802 String testName = currentName.replace('/', '.'); 803 testName = testName.replace('\\', '.'); 804 if (files != null) 805 { 806 Iterator i = files.iterator(); 807 boolean founded = false; 808 while (i.hasNext()) 809 { String doInclude = (String ) i.next(); 811 if (testName.matches(doInclude)) 812 { 813 founded = true; 814 break; 815 } 816 } 817 if (!founded) continue; 818 } 819 if (currentSet.contains(currentName)) continue; 820 try 821 { 822 out.putNextEntry(new ZipEntry (currentName)); 823 copyStream(zin, out); 824 out.closeEntry(); 825 zin.closeEntry(); 826 currentSet.add(currentName); 827 } 828 catch (ZipException x) 829 { 830 } 834 } 835 } 836 837 846 private void copyZipWithoutExcludes(ZipInputStream zin, ZipOutputStream out, List excludes) throws IOException 847 { 848 java.util.zip.ZipEntry zentry; 849 if (!alreadyWrittenFiles.containsKey(out)) alreadyWrittenFiles.put(out, new HashSet ()); 850 HashSet currentSet = (HashSet ) alreadyWrittenFiles.get(out); 851 while ((zentry = zin.getNextEntry()) != null) 852 { 853 String currentName = zentry.getName(); 854 String testName = currentName.replace('/', '.'); 855 testName = testName.replace('\\', '.'); 856 if (excludes != null) 857 { 858 Iterator i = excludes.iterator(); 859 boolean skip = false; 860 while (i.hasNext()) 861 { 862 String doExclude = (String ) i.next(); 864 if (testName.matches(doExclude)) 865 { 866 skip = true; 867 break; 868 } 869 } 870 if (skip){ 871 continue; 872 } 873 } 874 if (currentSet.contains(currentName)) continue; 875 try 876 { 877 out.putNextEntry(new ZipEntry (currentName)); 878 copyStream(zin, out); 879 out.closeEntry(); 880 zin.closeEntry(); 881 currentSet.add(currentName); 882 } 883 catch (ZipException x) 884 { 885 } 889 } 890 } 891 892 900 private long copyStream(InputStream in, OutputStream out) throws IOException 901 { 902 byte[] buffer = new byte[5120]; 903 long bytesCopied = 0; 904 int bytesInBuffer; 905 while ((bytesInBuffer = in.read(buffer)) != -1) 906 { 907 out.write(buffer, 0, bytesInBuffer); 908 bytesCopied += bytesInBuffer; 909 } 910 return bytesCopied; 911 } 912 913 918 public PackCompressor getCompressor() 919 { 920 return compressor; 921 } 922 923 public void setCompressorOptions(String compr_format, int compr_level) throws CompilerException 924 { 925 compressor = PackCompressorFactory.get(compr_format); 926 compressor.setCompressionLevel(compr_level); 927 } 928 929 public void addConfigurationInformation(XMLElement data) 930 { 931 this.configdata = data; 932 } 933 934 public void initPackCompressor(String compr_format, int compr_level) throws CompilerException 935 { 936 this.setCompressorOptions(compr_format, compr_level); 937 } 938 } | Popular Tags |