KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdesktop > jdic > packager > impl > MsiPackageGenerator


1 /*
2  * Copyright (C) 2004 Sun Microsystems, Inc. All rights reserved. Use is
3  * subject to license terms.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the Lesser GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA.
19  */

20  
21 package org.jdesktop.jdic.packager.impl;
22
23 import java.io.IOException JavaDoc;
24 import java.io.File JavaDoc;
25 import java.io.FileInputStream JavaDoc;
26 import java.io.FileOutputStream JavaDoc;
27 import java.util.TreeMap JavaDoc;
28 import java.util.ArrayList JavaDoc;
29
30 /**
31  * Concrete implementation of interface PackageGenerator for msi packages
32  * generating on Windows.
33  */

34 public class MsiPackageGenerator implements PackageGenerator {
35     /**
36      * The jar file containg these classes.
37      */

38     private static final String JavaDoc SYS_JAR_FILE_NAME = "packager.jar";
39     /**
40      * Jar file name for packaging all the jnlp relevant files.
41      */

42     private static final String JavaDoc JNLP_FILES_JAR_NAME = "JnlpFiles.jar";
43     /**
44      * Miscellanious MSI generatation needed files location in packager.jar.
45      */

46     private static final String JavaDoc MSI_SUPPORT_FILES_HIERARCHY_PATH =
47         "org/jdesktop/jdic/packager/impl/files/";
48     /**
49      * The relevant path of makecab.exe in MSSDKDir.
50      */

51     private static final String JavaDoc MAKECAB_EXE_PATH =
52         "Samples\\sysmgmt\\msi\\Patching\\makecab.exe";
53     /**
54      * The relevant path of msidb.exe in MSSDKDir.
55      */

56     private static final String JavaDoc MSIDB_EXE_PATH =
57         "bin\\msidb.exe";
58     /**
59      * Bootstrapper file path.
60      */

61     private static final String JavaDoc SYS_BOOTSTRAPPER_FILE_PATH =
62         MSI_SUPPORT_FILES_HIERARCHY_PATH + "bootstrapper.exe";
63     /**
64      * Custome dll file name.
65      */

66     private static final String JavaDoc CUSTOM_DLL_FILE_NAME = "custom.dll";
67     /**
68      * The text table containing the localized welcome message.
69      */

70     private static final String JavaDoc LOCALIZED_WELCOME_MSG_FILE_NAME =
71         "WelcomeMsg.idt";
72     /**
73      * The name of the table containing the localized welcome message.
74      */

75     public static final String JavaDoc LOCALIZED_WELCOME_MSG_TABLE_NAME =
76         "WelcomeMsg";
77     /**
78      * Default panel jpeg file name.
79      */

80     private static final String JavaDoc PANEL_JPG_FILE_NAME = "panel.jpg";
81     /**
82      * Banner jpeg file name.
83      */

84     private static final String JavaDoc BANNER_JPG_FILE_NAME = "banner.jpg";
85     /**
86      * Property for Java home directory.
87      */

88     private static final String JavaDoc JAVA_HOME_DIR_PROPERTY = "java.home";
89     /**
90      * Property for java class path.
91      */

92     private static final String JavaDoc SYS_CLASS_PATH = "java.class.path";
93     /**
94      * Property for author info.
95      */

96     private static final String JavaDoc AUTHOR_INFO = "http://jdic.dev.java.net";
97     /**
98      * Value index of field Text, table Control.
99      */

100     private static final int VI_CONTROL_TEXT = 10;
101     /**
102      * Value index of field Argument, table ControlEvent.
103      */

104     private static final int VI_CONTROLEVENT_ARGUMENT = 4;
105     /**
106      * Value index of field Event, table ControlEvent.
107      */

108     private static final int VI_CONTROLEVENT_EVENT = 3;
109     /**
110      * Value index of field Value, table Property.
111      */

112     private static final int VI_PROPERTY_VALUE = 2;
113     /**
114      * Property ID of property Revision Number, table Summary Info.
115      */

116     private static final int PID_REVISION_NUMBER = 9;
117     /**
118      * Property ID of property Author, table Summary Info.
119      */

120     private static final int PID_AUTHOR = 4;
121     /**
122      * Full path of file packager.jar.
123      */

124     private static String JavaDoc packagerJarFilePath = null;
125
126     /**
127      * MsiPackageGenerator creator.
128      *
129      */

130     public MsiPackageGenerator() {
131     }
132
133     /**
134      * Gets the location of packager.jar.
135      *
136      * @throws IOException If failed to get the location of packager.jar.
137      */

138     private static void getPackagerJarFilePath() throws IOException JavaDoc {
139         String JavaDoc sysClassPath = System.getProperty(SYS_CLASS_PATH);
140         int fileNameIndex = sysClassPath.indexOf(SYS_JAR_FILE_NAME);
141         int filePathIndex = -1;
142         int i = fileNameIndex;
143         char indexChar;
144         while (i > 0) {
145             i--;
146             indexChar = sysClassPath.charAt(i);
147             if ((indexChar == ';') || (i == 0)) {
148                 filePathIndex = i;
149                 break;
150             }
151         }
152         if (filePathIndex > 0) {
153             packagerJarFilePath =
154                 sysClassPath.substring(
155                     filePathIndex + 1,
156                     fileNameIndex + SYS_JAR_FILE_NAME.length());
157         } else if (filePathIndex == 0) {
158             packagerJarFilePath =
159                 sysClassPath.substring(
160                     filePathIndex,
161                     fileNameIndex + SYS_JAR_FILE_NAME.length());
162         } else {
163             throw new IOException JavaDoc(
164                     "Could not locate "
165                     + SYS_JAR_FILE_NAME
166                     + " in system CLASSPATH setting!");
167         }
168     }
169     
170     /**
171      * Modify template msi according to ShowLicense or not.
172      * @param msiFilePath The given template msi file.
173      * @param pkgInfo JnlpPackageInfo object.
174      * @param localeIndex The index of the given locale.
175      * @throws IOException If failed to modify the template msi file.
176      */

177     private static void adjustMsiLicenseFields(
178                         String JavaDoc msiFilePath,
179                         JnlpPackageInfo pkgInfo,
180                         int localeIndex) throws IOException JavaDoc {
181         TreeMap JavaDoc treeMap = new TreeMap JavaDoc();
182         File JavaDoc licenseFile;
183         FileInputStream JavaDoc fis;
184         //Set the license info field property
185
if (pkgInfo.getShowLicense()) {
186             try {
187                 String JavaDoc realLicenseInfo = new String JavaDoc();
188                 realLicenseInfo +=
189                 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deftab720{\\fonttbl{\\f0\\"
190                 + "froman\\fprq2 Times New Roman;}}{\\colortbl\\red0\\green0\\"
191                 + "blue0;} \\deflang1033\\horzdoc{\\*\\fchars }{\\*\\lchars }\\"
192                 + "pard\\plain\\f0\\fs20";
193
194                 // Get info from file
195
licenseFile = new File JavaDoc(
196                         pkgInfo.getLicenseDirPath()
197                         + File.separator
198                         + JnlpConstants.LOCALES[localeIndex]
199                         + ".txt");
200
201                 if (!licenseFile.exists()) {
202                     licenseFile =
203                         new File JavaDoc(
204                                 pkgInfo.getLicenseDirPath()
205                                 + File.separator
206                                 + JnlpConstants.LOCALES[JnlpConstants.LOCALE_EN]
207                                 + ".txt");
208                 }
209
210                 fis = new FileInputStream JavaDoc(licenseFile);
211                 int fLen = (int) licenseFile.length();
212                 byte[] bits = new byte[fLen];
213                 fis.read(bits, 0, fLen);
214                 fis.close();
215
216                 realLicenseInfo += new String JavaDoc(bits);
217                 realLicenseInfo += "\\par }";
218                 treeMap.clear();
219                 treeMap.put("AgreementText", realLicenseInfo);
220                 WinMsiUtility.winMsiSetProperty(
221                         msiFilePath,
222                         "Control",
223                         "Control",
224                         VI_CONTROL_TEXT,
225                         false,
226                         treeMap);
227             } catch (IOException JavaDoc e) {
228                 throw new IOException JavaDoc(
229                         "License Agreement: Modify Control Table failed: "
230                         + "Set MSI LicenseAgreement field property failed!");
231             }
232
233             try {
234                 treeMap.clear();
235                 treeMap.put("AfterWelcomeDlg", "LicenseAgreementDlg");
236                 WinMsiUtility.winMsiSetProperty(
237                         msiFilePath,
238                         "ControlEvent",
239                         "Argument",
240                         VI_CONTROLEVENT_ARGUMENT,
241                         false,
242                         treeMap);
243             } catch (IOException JavaDoc e) {
244                 throw new IOException JavaDoc(
245                         "License Agreement: Modify ControlEvent Table failed: "
246                         + "Insert License Agreement dialog failed!");
247             }
248         } else {
249             try {
250                 treeMap.clear();
251                 treeMap.put("AfterWelcomeDlg", "EndDialog");
252                 WinMsiUtility.winMsiSetProperty(
253                         msiFilePath,
254                         "ControlEvent",
255                         "Argument",
256                         VI_CONTROLEVENT_EVENT,
257                         false,
258                         treeMap);
259             } catch (IOException JavaDoc e) {
260                 throw new IOException JavaDoc(
261                     "No License Agreement: Modify ControlEvent Table failed: "
262                     + "AfterWelcomeDlg/EndDialog");
263             }
264
265             try {
266                 treeMap.clear();
267                 treeMap.put("AfterWelcomeDlg", "Return");
268                 WinMsiUtility.winMsiSetProperty(
269                         msiFilePath,
270                         "ControlEvent",
271                         "Argument",
272                         VI_CONTROLEVENT_ARGUMENT,
273                         false,
274                         treeMap);
275             } catch (IOException JavaDoc e) {
276                 throw new IOException JavaDoc(
277                     "No License Agreement: Modify ControlEvent Table failed: "
278                     + "AfterWelcomeDlg/Return");
279             }
280         }
281         //Setting proper welcome message for the control table.
282
//Try to get the welcomemsg for current locole.
283
String JavaDoc welcomeMsg = WinMsiUtility.getWelcomeMsg(
284                                 msiFilePath,
285                                 JnlpConstants.LOCALES[localeIndex],
286                                 pkgInfo.getShowLicense());
287         try {
288             treeMap.clear();
289             treeMap.put("SunButton", "[ButtonText_Next]");
290             // get relavant info from file
291
treeMap.put(
292                 "WelcomeDescription",
293                 welcomeMsg);
294             WinMsiUtility.winMsiSetProperty(
295                     msiFilePath,
296                     "Control",
297                     "Text",
298                     VI_CONTROL_TEXT,
299                     false,
300                     treeMap);
301         } catch (IOException JavaDoc e) {
302             throw new IOException JavaDoc(
303                 "Handling Agreement: Modify Control Table failed: Next "
304                 + "Button/WelcomeDescription");
305         }
306     }
307
308     /**
309      * Customize the non-localized MSI fields based on the JnlpPackageInfo.
310      * @param msiFilePath The given template msi file.
311      * @param pkgInfo JnlpPackageInfo object.
312      * @throws IOException If failed to modify the MSI file.
313      */

314     private static void customizeNonLocalizedMsiFields(String JavaDoc msiFilePath,
315                     JnlpPackageInfo pkgInfo) throws IOException JavaDoc {
316         TreeMap JavaDoc treeMap = new TreeMap JavaDoc();
317         // get uuid of msi
318
String JavaDoc uuidProduct = "{" + WinMsiUtility.genUUID() + "}";
319         String JavaDoc uuidUpgrade = "{" + WinMsiUtility.genUUID() + "}";
320
321         //Set property field of property table in the destination msi file
322
try {
323             treeMap.clear();
324             treeMap.put("ARPCONTACT", AUTHOR_INFO);
325             treeMap.put("ARPHELPLINK", AUTHOR_INFO);
326             treeMap.put("ARPURLINFOABOUT", AUTHOR_INFO);
327             treeMap.put("ARPURLUPDATEINFO", AUTHOR_INFO);
328             treeMap.put(
329                 "ARPCOMMENTS",
330                 "Generated by JDIC Project");
331
332             treeMap.put("ProductCode", uuidProduct);
333             treeMap.put("UpgradeCode", uuidUpgrade);
334             //Will put release # together with version #
335
treeMap.put(
336                 "ProductVersion",
337                 pkgInfo.getVersion() +
338                 "." +
339                 pkgInfo.getRelease());
340             treeMap.put("JnlpFileName", pkgInfo.getJnlpFileName());
341             treeMap.put("UninstallInfo", pkgInfo.getJnlpFileHref());
342
343             treeMap.put(
344                 "Shortcut",
345                 pkgInfo.getShortcutEnabled() ? "1" : "0");
346             treeMap.put(
347                 "Association",
348                 pkgInfo.getAssociationEnabled() ? "1" : "0");
349
350             treeMap.put(
351                 "CacheType",
352                 pkgInfo.getSystemCacheEnabled() ? "system" : "user");
353
354             WinMsiUtility.winMsiSetProperty(
355                 msiFilePath,
356                 "Property",
357                 "Property",
358                 VI_PROPERTY_VALUE,
359                 false,
360                 treeMap);
361         } catch (IOException JavaDoc e) {
362             throw new IOException JavaDoc("Set MSI field property failed!");
363         }
364
365         //Set the destination msi file summary information stream
366
try {
367             WinMsiUtility.setSummaryInfoProperty(msiFilePath,
368                                                  PID_REVISION_NUMBER,
369                                                  uuidProduct);
370             WinMsiUtility.setSummaryInfoProperty(msiFilePath,
371                                                  PID_AUTHOR,
372                                                  AUTHOR_INFO);
373         } catch (IOException JavaDoc e) {
374             throw new IOException JavaDoc("Set MSI summary information stream failed!");
375         }
376
377         String JavaDoc[] names = new String JavaDoc[] {"Name", "Data"};
378         String JavaDoc[] properties = new String JavaDoc[] {"String", "Stream"};
379         String JavaDoc[] values = new String JavaDoc[2];
380         try {
381             // Insert into "Binary" table, "CustomDll" record
382
String JavaDoc customDLLFilePath =
383                 pkgInfo.getUniqueTmpDirPath() + CUSTOM_DLL_FILE_NAME;
384             WinUtility.extractFileFromJarFile(
385                 packagerJarFilePath,
386                 MSI_SUPPORT_FILES_HIERARCHY_PATH + CUSTOM_DLL_FILE_NAME,
387                 customDLLFilePath);
388             values[0] = "CustomDLL";
389             values[1] = customDLLFilePath;
390             WinMsiUtility.addBinaryRecord(
391                     msiFilePath,
392                     "Binary",
393                     names,
394                     properties,
395                     values);
396         } catch (IOException JavaDoc e) {
397             throw new IOException JavaDoc("Binary: insert CustomDLL field failed!");
398         }
399
400         try {
401             // Insert into "Binary" table, "JarPack" record
402
String JavaDoc tempJarFilePath = pkgInfo.getUniqueTmpDirPath()
403                                      + JNLP_FILES_JAR_NAME;
404             WinUtility.jarJnlpFiles(tempJarFilePath, pkgInfo);
405             values[0] = "JarPack";
406             values[1] = tempJarFilePath;
407             WinMsiUtility.addBinaryRecord(
408                     msiFilePath,
409                     "Binary",
410                     names,
411                     properties,
412                     values);
413         } catch (IOException JavaDoc e) {
414             throw new IOException JavaDoc("Binary: insert JarPack field failed!");
415         }
416
417         treeMap.clear();
418         try {
419             // Modify "Binary" table, update "bannerBmp"
420
String JavaDoc bannerJpgFilePath =
421                 pkgInfo.getUniqueTmpDirPath() + BANNER_JPG_FILE_NAME;
422             if (pkgInfo.getBannerJpgFilePath() != null) {
423                 bannerJpgFilePath = pkgInfo.getBannerJpgFilePath();
424             } else {
425                 WinUtility.extractFileFromJarFile(
426                     packagerJarFilePath,
427                     MSI_SUPPORT_FILES_HIERARCHY_PATH + BANNER_JPG_FILE_NAME,
428                     bannerJpgFilePath);
429             }
430             treeMap.put("bannrbmp", bannerJpgFilePath);
431         } catch (IOException JavaDoc e) {
432             throw new IOException JavaDoc("Binary: pre insert bannrbmp field failed!");
433         }
434
435         try {
436             // Modify "Binary" table, update "panelBmp"
437
String JavaDoc panelJpgFilePath = pkgInfo.getUniqueTmpDirPath()
438                                     + PANEL_JPG_FILE_NAME;
439             if (pkgInfo.getPanelJpgFilePath() != null) {
440                 panelJpgFilePath = pkgInfo.getPanelJpgFilePath();
441             } else {
442                 WinUtility.extractFileFromJarFile(
443                         packagerJarFilePath,
444                         MSI_SUPPORT_FILES_HIERARCHY_PATH + PANEL_JPG_FILE_NAME,
445                         panelJpgFilePath);
446             }
447             treeMap.put("dlgbmp", panelJpgFilePath);
448         } catch (IOException JavaDoc e) {
449             throw new IOException JavaDoc("Binary: pre insert dlgbmp field failed!");
450         }
451
452         try {
453             WinMsiUtility.winMsiSetProperty(
454                     msiFilePath,
455                     "Binary",
456                     "Name",
457                     2,
458                     true,
459                     treeMap);
460         } catch (IOException JavaDoc e) {
461             throw new IOException JavaDoc(
462                         "Binary: insert bannrbmp and dlgbmp field failed!");
463         }
464 }
465
466     /**
467      * Generate the template MSI file based on the rawMSI file.
468      * @param msiFilePath The given raw msi file.
469      * @param pkgInfo JnlpPackageInfo object.
470      * @throws IOException If failed to generate the template msi file.
471      */

472     private static void createTemplateMsiFile(String JavaDoc msiFilePath,
473                             JnlpPackageInfo pkgInfo) throws IOException JavaDoc {
474         // Modify the template msi file
475
ArrayList JavaDoc sqlStrings = new ArrayList JavaDoc();
476         //Add table Directory
477
sqlStrings.add("CREATE TABLE Directory (Directory CHAR(72) NOT NULL, "
478                        + "Directory_Parent CHAR(72), DefaultDir CHAR(255) NOT "
479                        + "NULL LOCALIZABLE PRIMARY KEY Directory)");
480         sqlStrings.add("INSERT INTO Directory (Directory, Directory_Parent, "
481                        + "DefaultDir) VALUES ('TARGETDIR', '', 'SourceDir')");
482         sqlStrings.add("INSERT INTO Directory (Directory, Directory_Parent, "
483                        + "DefaultDir) VALUES ('ProgramFilesFolder', "
484                        + "'TARGETDIR', '.:PROGRA~1|program files')");
485         sqlStrings.add("INSERT INTO Directory (Directory, Directory_Parent, "
486                        + "DefaultDir) VALUES ('INSTALLDIR', 'TempFolder', "
487                        + "'.')");
488         sqlStrings.add("INSERT INTO Directory (Directory, Directory_Parent, "
489                        + "DefaultDir) VALUES ('TempFolder', 'TARGETDIR', "
490                        + "'.:Temp')");
491         //Add table Component
492
sqlStrings.add("CREATE TABLE Component (Component CHAR(72) NOT NULL, "
493                        + "ComponentId CHAR(38), Directory_ CHAR(72) NOT NULL, "
494                        + "Attributes INT NOT NULL, Condition CHAR(255), "
495                        + "KeyPath CHAR(72) PRIMARY KEY Component)");
496         sqlStrings.add("INSERT INTO Component (Component, ComponentId, "
497                        + "Directory_, Attributes, Condition, KeyPath) VALUES "
498                        + "('AllOtherFiles', "
499                        + "'{31CCF52F-FCB3-4239-9D82-05CDA204867A}', "
500                        + "'INSTALLDIR', 8, '', '')");
501         //Add table File
502
sqlStrings.add("CREATE TABLE File (File CHAR(72) NOT NULL, Component_ "
503                        + "CHAR(72) NOT NULL, FileName CHAR(255) NOT NULL "
504                        + "LOCALIZABLE, FileSize LONG NOT NULL, "
505                        + "Version CHAR(72), Language CHAR(20), Attributes INT, "
506                        + "Sequence INT NOT NULL PRIMARY KEY File)");
507         sqlStrings.add("INSERT INTO File (File, Component_, FileName, "
508                        + "FileSize, Version, Language, Attributes, Sequence) "
509                        + "VALUES ('place.txt', 'AllOtherFiles', 'place.txt', "
510                        + "18, '', '', 16384, 1)");
511         //Add table Media
512
sqlStrings.add("CREATE TABLE Media (DiskId INT NOT NULL, LastSequence "
513                        + "INT NOT NULL, DiskPrompt CHAR(64) LOCALIZABLE, "
514                        + "Cabinet CHAR(255), VolumeLabel CHAR(32), Source "
515                        + "CHAR(72) PRIMARY KEY DiskId)");
516         sqlStrings.add("INSERT INTO Media (DiskId, LastSequence, DiskPrompt, "
517                        + "Cabinet, VolumeLabel, Source) VALUES (1, 1, '', "
518                        + "'#Data1.cab', '', '')");
519         //Add table Feature
520
sqlStrings.add("CREATE TABLE Feature (Feature CHAR(38) NOT NULL, "
521                        + "Feature_Parent CHAR(38), Title CHAR(64) LOCALIZABLE, "
522                        + "Description CHAR(255) LOCALIZABLE, Display INT, "
523                        + "Level INT NOT NULL, Directory_ CHAR(72), Attributes "
524                        + "INT NOT NULL PRIMARY KEY Feature)");
525         sqlStrings.add("INSERT INTO Feature (Feature, Feature_Parent, Title, "
526                        + "Description, Display, Level, Directory_ , "
527                        + "Attributes) VALUES ('NewFeature1', '', "
528                        + "'NewFeature1', '', 2, 1, 'INSTALLDIR', 0)");
529         //Add table FeatureComponents
530
sqlStrings.add("CREATE TABLE FeatureComponents (Feature_ CHAR(38) NOT "
531                        + "NULL, Component_ CHAR(72) NOT NULL PRIMARY KEY "
532                        + "Feature_, Component_)");
533         sqlStrings.add("INSERT INTO FeatureComponents (Feature_, Component_) "
534                        + "VALUES ('NewFeature1', 'AllOtherFiles')");
535         //Add table CustomActioni
536
sqlStrings.add("CREATE TABLE CustomAction(Action CHAR(72) NOT NULL, "
537                        + "Type INT NOT NULL, Source CHAR(72), Target CHAR(255) "
538                        + "PRIMARY KEY Action)");
539         sqlStrings.add("INSERT INTO CustomAction (Action,Type,Source,Target) "
540                        + "VALUES ('CustomInstallAction', 1, 'CustomDLL', "
541                        + "'InstallAction')");
542         sqlStrings.add("INSERT INTO CustomAction (Action,Type,Source,Target) "
543                        + "VALUES ('CustomUninstallAction', 1, 'CustomDLL', "
544                        + "'UninstallAction')");
545         //Modify table property
546
sqlStrings.add("INSERT INTO Property (Property,Value) VALUES "
547                        + "('Manufacturer','" + AUTHOR_INFO + "')");
548         sqlStrings.add("INSERT INTO Property (Property,Value) VALUES "
549                        + "('ProductCode', "
550                        + "'{3D11E9FC-142F-4945-8010-861EAA24850F}')");
551         sqlStrings.add("INSERT INTO Property (Property,Value) "
552                        + "VALUES ('ProductName','Default')");
553         sqlStrings.add("INSERT INTO Property (Property,Value) "
554                        + "VALUES ('ProductVersion','1.00.0000')");
555         sqlStrings.add("INSERT INTO Property (Property,Value) "
556                        + "VALUES ('UpgradeCode', "
557                        + "'{20AC1669-B481-4CAD-82AF-2CD00005F304}')");
558         sqlStrings.add("INSERT INTO Property (Property,Value) VALUES "
559                        + "('JnlpFileName','ok')");
560         sqlStrings.add("INSERT INTO Property (Property,Value) VALUES "
561                        + "('UninstallInfo','ok')");
562         sqlStrings.add("INSERT INTO Property (Property,Value) VALUES "
563                        + "('Locale','en')");
564         sqlStrings.add("UPDATE Property SET Value='" + AUTHOR_INFO
565                        + "' where Property='ARPHELPLINK'");
566         sqlStrings.add("UPDATE Property SET Value='" + AUTHOR_INFO
567                        + "' where Property='ComponentDownload'");
568         sqlStrings.add("INSERT INTO Property (Property,Value) VALUES "
569                        + "('ARPCONTACT','" + AUTHOR_INFO + "')");
570         sqlStrings.add("INSERT INTO Property (Property,Value) VALUES "
571                        + "('ARPURLINFOABOUT','" + AUTHOR_INFO + "')");
572         sqlStrings.add("INSERT INTO Property (Property,Value) VALUES "
573                        + "('ARPURLUPDATEINFO','" + AUTHOR_INFO + "')");
574         sqlStrings.add("INSERT INTO Property (Property,Value) VALUES "
575                        + "('ARPCOMMENTS','Generated by JDIC Project')");
576         sqlStrings.add("INSERT INTO Property (Property,Value) VALUES "
577                        + "('Shortcut','yes')");
578         sqlStrings.add("INSERT INTO Property (Property,Value) VALUES "
579                        + "('Association','yes')");
580         sqlStrings.add("INSERT INTO Property (Property,Value) VALUES "
581                        + "('CacheType','user')");
582
583         //Modify table InstallExecuteSequence
584
sqlStrings.add("INSERT INTO InstallExecuteSequence (Action, Condition, "
585                        + "Sequence) VALUES ('CustomInstallAction', "
586                        + "'NOT Installed', 6650)");
587         sqlStrings.add("INSERT INTO InstallExecuteSequence (Action, Condition, "
588                        + "Sequence) VALUES ('CustomUninstallAction', "
589                        + "'Installed', 6651)");
590         //Modify table Dialog
591
sqlStrings.add("UPDATE Dialog SET Control_Cancel='RemoveButton' where "
592                        + "Dialog='MaintenanceTypeDlg'");
593         //Modify table Control
594
sqlStrings.add("UPDATE Control SET Attributes=0 where "
595                        + "Dialog_='MaintenanceTypeDlg' and "
596                        + "Control='ChangeLabel'");
597         sqlStrings.add("UPDATE Control SET Attributes=5767168 where "
598                        + "Dialog_='MaintenannceTypeDlg' and "
599                        + "Control='ChangeButton'");
600         sqlStrings.add("UPDATE Control SET Attributes=0 where "
601                        + "Dialog_='MaintenanceTypeDlg' and "
602                        + "Control='ChangeText'");
603         sqlStrings.add("UPDATE Control SET Attributes=0 where "
604                        + "Dialog_='MaintenanceTypeDlg' and "
605                        + "Control='RepairLabel'");
606         sqlStrings.add("UPDATE Control SET Attributes=5767168 where "
607                        + "Dialog_='MaintenanceTypeDlg' and "
608                        + "Control='RepairButton'");
609         sqlStrings.add("UPDATE Control SET Attributes=0 where "
610                        + "Dialog_='MaintenanceTypeDlg' and "
611                        + "Control='RepairText'");
612         sqlStrings.add("UPDATE Control SET Y=65 where "
613                        + "Dialog_='MaintenanceTypeDlg' and "
614                        + "Control='RemoveButton'");
615         sqlStrings.add("UPDATE Control SET Y=65 where "
616                        + "Dialog_='MaintenanceTypeDlg' and "
617                        + "Control='RemoveLabel'");
618         sqlStrings.add("UPDATE Control SET Y=78 where "
619                        + "Dialog_='MaintenanceTypeDlg' and "
620                        + "Control='RemoveText'");
621         sqlStrings.add("UPDATE Control SET Y=78 where "
622                        + "Dialog_='MaintenanceTypeDlg' and "
623                        + "Control='RemoveText'");
624         sqlStrings.add("UPDATE Control SET "
625                        + "Text='[DlgTitleFont]Remove installation' where "
626                        + "Dialog_='MaintenanceTypeDlg' and Control='Title'");
627         sqlStrings.add("UPDATE Control SET Attributes=196608 where "
628                        + "Dialog_='MaintenanceTypeDlg' and "
629                        + "Control='Description'");
630         sqlStrings.add("UPDATE Control SET Text='The [Wizard] is ready to "
631                        + "begin the installation' where "
632                        + "Dialog_='VerifyReadyDlg' and Control='Description'");
633         sqlStrings.add("UPDATE Control SET Text='Click Install to begin the "
634                        + "installation. Click Cancel to exit the wizard.' "
635                        + "where Dialog_='VerifyReadyDlg' and Control='Text'");
636         sqlStrings.add("UPDATE Control SET Text='The [Wizard] will allow you to"
637                        + "remove [ProductName] from your computer. Click Next "
638                        + "to continue or Cancel to exit the [Wizard].' where "
639                        + "Dialog_='MaintenanceWelcomeDlg' and "
640                        + "Control='Description'");
641         sqlStrings.add("UPDATE Control SET Text='Click Remove to remove "
642                        + "[ProductName] from your computer. Click Cancel to "
643                        + "exit the wizard.' where Dialog_='VerifyRemoveDlg' "
644                        + "and Control='Text'");
645         sqlStrings.add("UPDATE Control SET Text='{\\DlgFontBold8}Remove "
646                        + "[ProductName]' where Dialog_='VerifyRemoveDlg' "
647                        + "and Control='Title'");
648         sqlStrings.add("UPDATE Control SET Text='SunButton' where "
649                        + "Dialog_='WelcomeDlg' and Control='Next'");
650         sqlStrings.add("UPDATE Control SET Text='WelcomeDescription' where "
651                        + "Dialog_='WelcomeDlg' and Control='Description'");
652         sqlStrings.add("UPDATE Control SET Text='[ButtonText_Remove]' where "
653                        + "Dialog_='MaintenanceWelcomeDlg' and Control='Next'");
654         sqlStrings.add("UPDATE Control SET Text='The [Wizard] will allow you "
655                        + "to remove [ProductName] from your computer. Click "
656                        + "Remove to continue or Cancel to exit the [Wizard].' "
657                        + "where Dialog_='MaintenanceWelcomeDlg' and "
658                        + "Control='Description'");
659         // Modify the ControlEvent table
660
sqlStrings.add("DELETE FROM ControlEvent where Dialog_='WelcomeDlg' "
661                        + "and Control_='Next'");
662         sqlStrings.add("INSERT INTO ControlEvent (Dialog_, Control_, Event, "
663                        + "Argument, Condition) VALUES ('WelcomeDlg', 'Next', "
664                        + "'NewDialog', 'AfterWelcomeDlg', 1)");
665         sqlStrings.add("DELETE from ControlEvent where "
666                        + "Dialog_='LicenseAgreementDlg' and Control_='Next' "
667                        + "and Event='NewDialog'");
668         sqlStrings.add("INSERT INTO ControlEvent (Dialog_, Control_,Event, "
669                        + "Argument, Condition, Ordering) VALUES "
670                        + "('LicenseAgreementDlg', 'Next', 'EndDialog', "
671                        + "'Return', 'IAgree = \"Yes\"', 1)");
672         sqlStrings.add("DELETE FROM ControlEvent where "
673                        + "Dialog_='MaintenanceWelcomeDlg' and Control_='Next'");
674         sqlStrings.add("INSERT INTO ControlEvent (Dialog_, Control_, Event, "
675                        + "Argument, Condition, Ordering) VALUES "
676                        + "('MaintenanceWelcomeDlg', 'Next', '[InstallMode]', "
677                        + "'Remove', '1', 1)");
678         sqlStrings.add("INSERT INTO ControlEvent (Dialog_, Control_, Event, "
679                        + "Argument, Condition, Ordering) VALUES "
680                        + "('MaintenanceWelcomeDlg', 'Next', '[Progress1]', "
681                        + "'Removing', '1', 2)");
682         sqlStrings.add("INSERT INTO ControlEvent (Dialog_, Control_, Event, "
683                        + "Argument, Condition, Ordering) VALUES "
684                        + "('MaintenanceWelcomeDlg', 'Next', '[Progress2]', "
685                        + "'removes', '1', 3)");
686         sqlStrings.add("INSERT INTO ControlEvent (Dialog_, Control_, Event, "
687                        + "Argument, Condition, Ordering) VALUES "
688                        + "('MaintenanceWelcomeDlg', 'Next', 'SpawnWaitDialog', "
689                        + "'WaitForCostingDlg', 'CostingComplete = 1', 4)");
690         sqlStrings.add("INSERT INTO ControlEvent (Dialog_, Control_, Event, "
691                        + "Argument, Condition, Ordering) VALUES "
692                        + "('MaintenanceWelcomeDlg', 'Next', 'Remove', "
693                        + "'All', '1', 5)");
694         sqlStrings.add("INSERT INTO ControlEvent (Dialog_, Control_, Event, "
695                        + "Argument, Condition, Ordering) VALUES "
696                        + "('MaintenanceWelcomeDlg', 'Next', 'EndDialog', "
697                        + "'Return', '1', 6)");
698         WinMsiUtility.runSql(msiFilePath, sqlStrings);
699
700         // Create a temp place.txt file with certain content in it.
701
File JavaDoc tempTxtFile = new File JavaDoc(pkgInfo.getUniqueTmpDirPath()
702                                     + "place.txt");
703         FileOutputStream JavaDoc fis = new FileOutputStream JavaDoc(tempTxtFile);
704         String JavaDoc contentString = new String JavaDoc(AUTHOR_INFO);
705         byte[] bits = contentString.getBytes();
706         fis.write(bits, 0, contentString.length());
707         fis.close();
708
709         // Insert the place.txt into msi, in form of cab
710
// Evaluate if makecab.exe and msidb.exe exist in MSSDKPath.
711
String JavaDoc makecabFullPath = pkgInfo.getMSSDKDirPath() + MAKECAB_EXE_PATH;
712         String JavaDoc msidbFullPath = pkgInfo.getMSSDKDirPath() + MSIDB_EXE_PATH;
713         File JavaDoc temFile = new File JavaDoc(makecabFullPath);
714         if (!temFile.exists()) {
715             throw new IOException JavaDoc("Can not locate makecab.exe at" +
716                                    makecabFullPath);
717         }
718         temFile = new File JavaDoc(msidbFullPath);
719         if (!temFile.exists()) {
720             throw new IOException JavaDoc(
721                 "Can not locate msidb.exe at" +
722                 msidbFullPath);
723         }
724         String JavaDoc tempCabFilePath = pkgInfo.getUniqueTmpDirPath() + "Data1.cab";
725         Process JavaDoc proc = Runtime.getRuntime().exec(
726                 "\"" + makecabFullPath + "\" " + tempTxtFile.toString()
727                 + " " + tempCabFilePath);
728         try {
729             proc.waitFor();
730         } catch (InterruptedException JavaDoc e) {
731             throw new IOException JavaDoc(e.getMessage());
732         }
733         proc = Runtime.getRuntime().exec(
734                 "\"" + msidbFullPath + "\" -d " + msiFilePath +
735                 " -a " + tempCabFilePath);
736         try {
737             proc.waitFor();
738         } catch (InterruptedException JavaDoc e) {
739             throw new IOException JavaDoc(e.getMessage());
740         }
741         
742         //Import the localized WelcomeMsg table into the template MSI file.
743
String JavaDoc tempWelcomeMsgFilePath = pkgInfo.getUniqueTmpDirPath()
744                                         + LOCALIZED_WELCOME_MSG_FILE_NAME;
745         String JavaDoc sysWelcomeMsgFilePath = MSI_SUPPORT_FILES_HIERARCHY_PATH
746                                         + LOCALIZED_WELCOME_MSG_FILE_NAME;
747         WinUtility.extractFileFromJarFile(
748                     packagerJarFilePath,
749                     sysWelcomeMsgFilePath,
750                     tempWelcomeMsgFilePath);
751         WinMsiUtility.importTableFromFile(
752                     msiFilePath,
753                     pkgInfo.getUniqueTmpDirPath(),
754                     LOCALIZED_WELCOME_MSG_FILE_NAME);
755     }
756
757     /**
758      * Generates the msi installation package.
759      *
760      * @param pkgInfo The given jnlp package info.
761      * @throws IOException If failed to generate the MSi package.
762      */

763     public final void generatePackage(JnlpPackageInfo pkgInfo)
764                 throws IOException JavaDoc {
765         //get the location of packager.jar
766
getPackagerJarFilePath();
767         // Get common Info for all locale packages
768

769         // Copy user defined raw Msi file to the temp dir as en.msi
770
String JavaDoc enMsiFilePath = pkgInfo.getUniqueTmpDirPath() + "en.msi";
771         String JavaDoc rawMsiFilePath = pkgInfo.getRawMsiFilePath();
772         FileOperUtility.copyLocalFile(rawMsiFilePath, enMsiFilePath);
773
774         //Generate the en.msi
775
createTemplateMsiFile(enMsiFilePath, pkgInfo);
776         customizeNonLocalizedMsiFields(enMsiFilePath, pkgInfo);
777         //Put JavaWS product name into MSI file
778
putProductNameIntoMsi(enMsiFilePath, pkgInfo, JnlpConstants.LOCALE_EN);
779         // Adjust the relevant fields to reflect current show license setting
780
adjustMsiLicenseFields(enMsiFilePath, pkgInfo, JnlpConstants.LOCALE_EN);
781
782         //Generate the localized MSI file if required
783
if (pkgInfo.getLocalizationEnabled()) {
784             // Genreate a locale-specific msi file in every loop
785
for (int i = 1; i < JnlpConstants.LOCALES.length; i++) {
786                 // Copy en.msi to localized msi file
787
String JavaDoc localizedMsiFilePath =
788                     pkgInfo.getUniqueTmpDirPath()
789                         + JnlpConstants.LOCALES[i]
790                         + ".msi";
791                 FileOperUtility.copyLocalFile(enMsiFilePath,
792                         localizedMsiFilePath);
793
794                 //Import the localized tables into target MSI file
795
importLocalizedTables(localizedMsiFilePath, pkgInfo, i);
796
797                 //Put localized JavaWS product name into MSI file
798
putProductNameIntoMsi(localizedMsiFilePath, pkgInfo, i);
799
800                 // Adjust the relevant fields to reflect current show
801
// license setting
802
adjustMsiLicenseFields(localizedMsiFilePath, pkgInfo, i);
803             }
804         }
805         generateBootStrapper(pkgInfo);
806     }
807
808     /**
809      * Import the localized MSI tables into the target MSI file, includes
810      * ActionText, Error, Control & ControlEvent.
811      * @param localizedMsiFilePath The localized MSI file path.
812      * @param pkgInfo The given JNLP package info.
813      * @param localeIndex The index of the current locale
814      * @throws IOException If failed to import these tables.
815      */

816     public final void importLocalizedTables(String JavaDoc localizedMsiFilePath,
817                                       JnlpPackageInfo pkgInfo,
818                                       int localeIndex) throws IOException JavaDoc {
819         ArrayList JavaDoc sqlStrings = new ArrayList JavaDoc();
820         // If Localization support enabled, we will import the
821
// "ActionText", "Error", "Control", "ControlEvent"
822
sqlStrings.add("DROP TABLE ActionText");
823         sqlStrings.add("DROP TABLE Error");
824         sqlStrings.add("DROP TABLE Control");
825         sqlStrings.add("DROP TABLE ControlEvent");
826
827         WinMsiUtility.runSql(localizedMsiFilePath, sqlStrings);
828
829         //Extract the tables from jar files
830
String JavaDoc localizedControlTableFileName =
831                 "Control." + JnlpConstants.LOCALES_SUFFIX[localeIndex];
832         String JavaDoc localizedControlTableFilePath =
833                 pkgInfo.getUniqueTmpDirPath() + localizedControlTableFileName;
834         String JavaDoc localizedControlEventTableFileName =
835                 "ControlEvent." + JnlpConstants.LOCALES_SUFFIX[localeIndex];
836         String JavaDoc localizedControlEventTableFilePath =
837                 pkgInfo.getUniqueTmpDirPath()
838                 + localizedControlEventTableFileName;
839
840         WinUtility.extractFileFromJarFile(
841             packagerJarFilePath,
842             MSI_SUPPORT_FILES_HIERARCHY_PATH + localizedControlTableFileName,
843             localizedControlTableFilePath);
844
845         WinUtility.extractFileFromJarFile(
846             packagerJarFilePath,
847             MSI_SUPPORT_FILES_HIERARCHY_PATH
848             + localizedControlEventTableFileName,
849             localizedControlEventTableFilePath);
850
851         // Import the ActionText table
852
WinMsiUtility.importTableFromFile(
853             localizedMsiFilePath,
854             pkgInfo.getUniqueTmpDirPath(),
855             "ActionTe." + JnlpConstants.LOCALES_SUFFIX[localeIndex]);
856
857         // Import the Error table
858
WinMsiUtility.importTableFromFile(
859                 localizedMsiFilePath,
860                 pkgInfo.getUniqueTmpDirPath(),
861                 "Error" + JnlpConstants.LOCALES_SUFFIX[localeIndex]);
862
863         //Import the Control table
864
WinMsiUtility.importTableFromFile(
865                 localizedMsiFilePath,
866                 pkgInfo.getUniqueTmpDirPath(),
867                 localizedControlTableFileName);
868
869         //Import the Control event table
870
WinMsiUtility.importTableFromFile(
871                 localizedMsiFilePath,
872                 pkgInfo.getUniqueTmpDirPath(),
873                 localizedControlEventTableFileName);
874
875     }
876
877     /**
878      * Put localized product name into the MSI file.
879      *
880      * @param localizedMsiFilePath The localized MSI file path.
881      * @param pkgInfo The given jnlp package info.
882      * @param localeIndex The index of current locale.
883      * @throws IOException If failed to put the product name into MSI.
884      */

885     public final void putProductNameIntoMsi(String JavaDoc localizedMsiFilePath,
886                                             JnlpPackageInfo pkgInfo,
887                                             int localeIndex)
888                                             throws IOException JavaDoc {
889         TreeMap JavaDoc treeMap = new TreeMap JavaDoc();
890         try {
891             treeMap.clear();
892             treeMap.put(
893                 "ProductName",
894                 pkgInfo.getLocalizedJnlpInfo(
895                         JnlpConstants.LOCALES[localeIndex],
896                         JnlpConstants.JNLP_FIELD_TITLE));
897             treeMap.put("Locale", JnlpConstants.LOCALES[localeIndex]);
898             WinMsiUtility.winMsiSetProperty(
899                     localizedMsiFilePath,
900                     "Property",
901                     "Property",
902                     2,
903                     false,
904                     treeMap);
905         } catch (IOException JavaDoc e) {
906             throw new IOException JavaDoc(
907                 "Property Table: Modify ProductName field failed!");
908         }
909     }
910
911     /**
912      * Generate the target boot strapper file.
913      *
914      * @param pkgInfo The given package information.
915      * @todo Get the target bootstrapper file name
916      *
917      * @throws IOException If failed to generate the bootstrapper.
918      */

919     public final void generateBootStrapper(JnlpPackageInfo pkgInfo)
920         throws IOException JavaDoc {
921         //Get the MSI file name for locale en, The localized msi file naming
922
// schema would be locale.msi
923
//e.g. zh_cn.msi
924
String JavaDoc enMsiFilePath =
925             pkgInfo.getUniqueTmpDirPath()
926                 + JnlpConstants.LOCALES[JnlpConstants.LOCALE_EN]
927                 + ".msi";
928
929         //If there is localization requirement, we'll then generate the 9
930
//mst files and incorporate them into the en.msi
931
if (pkgInfo.getLocalizationEnabled()) {
932             // Genreate a locale-specific mst file in every loop, the msi file
933
// naming schema would be locale.mst, e.g. zh_cn.mst
934
String JavaDoc localizedMsiFilePath;
935             String JavaDoc targetMstFilePath;
936             for (int i = 1; i < JnlpConstants.LOCALES.length; i++) {
937                 localizedMsiFilePath =
938                     pkgInfo.getUniqueTmpDirPath()
939                         + JnlpConstants.LOCALES[i]
940                         + ".msi";
941                 targetMstFilePath =
942                     pkgInfo.getUniqueTmpDirPath()
943                         + JnlpConstants.LOCALES[i]
944                         + ".mst";
945                 WinMsiUtility.generateTransform(
946                     enMsiFilePath,
947                     localizedMsiFilePath,
948                     targetMstFilePath);
949             }
950
951             // Incorporate the 9 mst files into file en.msi, these mst files
952
// woule be insert into _Storage table with each name as
953
// cust_locale, e.g. cust_zh_cn, cust_fr.
954
String JavaDoc newFieldName;
955             for (int i = 1; i < JnlpConstants.LOCALES.length; i++) {
956                 targetMstFilePath =
957                     pkgInfo.getUniqueTmpDirPath()
958                         + JnlpConstants.LOCALES[i]
959                         + ".mst";
960                 newFieldName = "cust_" + JnlpConstants.LOCALES[i];
961                 WinMsiUtility.incorporateMST(
962                     enMsiFilePath,
963                     targetMstFilePath,
964                     newFieldName);
965             }
966         }
967
968         //Extract the bootstrapper from the jar file
969
String JavaDoc tempBootStrapperFilePath =
970             pkgInfo.getUniqueTmpDirPath() + pkgInfo.getPackageName() + ".exe";
971         WinUtility.extractFileFromJarFile(
972             packagerJarFilePath,
973             SYS_BOOTSTRAPPER_FILE_PATH,
974             tempBootStrapperFilePath);
975
976         //Store an UUID into the bootstrapper resource, which can be used to
977
// identify whether there
978
//are more than one instance running
979
String JavaDoc exeUUID = WinMsiUtility.genUUID();
980         WinUtility.updateResourceString(
981             tempBootStrapperFilePath,
982             exeUUID,
983             WinUtility.STRING_RES_UUID_ID);
984
985         //Incorporate the en.msi into the bootstrpper resources
986
WinUtility.updateResourceData(
987             tempBootStrapperFilePath,
988             enMsiFilePath,
989             WinUtility.BIN_RES_ID);
990
991         //Store a flag string indicating whether localization is supported
992
String JavaDoc localizationFlag;
993         if (pkgInfo.getLocalizationEnabled()) {
994             localizationFlag = "localization supported";
995         } else {
996             localizationFlag = "no localization support";
997         }
998         WinUtility.updateResourceString(
999             tempBootStrapperFilePath,
1000            localizationFlag,
1001            WinUtility.STRING_RES_LOCALIZATION_FLAG_ID);
1002
1003        //Copy the generated bootstrapper from temp dir to target dir
1004
String JavaDoc targetBootStrapperFilePath =
1005            pkgInfo.getOutputDirPath() + pkgInfo.getPackageName() + ".exe";
1006        FileOperUtility.copyLocalFile(tempBootStrapperFilePath,
1007                                 targetBootStrapperFilePath);
1008        //Indicate user where the file has been generated
1009
File JavaDoc targetFile = new File JavaDoc(targetBootStrapperFilePath);
1010        if (targetFile.exists()) {
1011            System.out.println("\nSuccess: The bootstrapper MSI file has been" +
1012                " generated at: \n" + targetBootStrapperFilePath + "\n");
1013        }
1014
1015        //Remove the Unique Tmp Dir
1016
File JavaDoc tempDir = new File JavaDoc(pkgInfo.getUniqueTmpDirPath());
1017        FileOperUtility.deleteDirTree(tempDir);
1018    }
1019}
1020
Popular Tags