KickJava   Java API By Example, From Geeks To Geeks.

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


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.File JavaDoc;
24 import java.io.FileWriter JavaDoc;
25 import java.io.BufferedWriter JavaDoc;
26 import java.io.InputStreamReader JavaDoc;
27 import java.io.BufferedReader JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.net.URL JavaDoc;
30 import java.net.MalformedURLException JavaDoc;
31
32   
33 /**
34  * Concrete implementation of interface PackageGenerator for pkg packages
35  * generating on Solaris.
36  * <pre>
37  * The steps to create installable packages are briefly as below:
38  * 1. Create "pkginfo" file at the location for the package information files.
39  * 2. Create "copyright" file at the location for the package information files.
40  * 3. Create "checkinstall" file at the location for the package information files.
41  * 4. Create "postinstall" file at the location for the package information files.
42  * 5. Create "postremove" file at the location for the package information files.
43  * 6. Generate 'prototype' file using command 'pkgproto' with the jnlp file,
44  * jnlp resource files, and the created information files including "pkginfo",
45  * "postinstall" and "postremove".
46  * 7. Generate an installable package using command 'pkgmk', with the created prototype file,
47  * created information files, the given jnlp file, and JNLP referenced resource files.
48  * </pre>
49  */

50 public class PkgPackageGenerator implements PackageGenerator {
51     // Information files used to generate installable packages in pkg format.
52
// These files are generated during the packaging process, and will be
53
// deleted automatically after the process finishes.
54
private static final String JavaDoc FILE_NAME_PKGINFO = "pkginfo";
55     private static final String JavaDoc FILE_NAME_COPYRIGHT = "copyright";
56     private static final String JavaDoc FILE_NAME_CHECKINSTALL = "checkinstall";
57     private static final String JavaDoc FILE_NAME_POSTINSTALL = "postinstall";
58     private static final String JavaDoc FILE_NAME_POSTREMOVE = "postremove";
59     private static final String JavaDoc FILE_NAME_PROTOTYPE = "prototype";
60     
61     // Required information files to generate a package.
62
File JavaDoc pkginfoFile = null;
63     File JavaDoc copyrightFile = null;
64     File JavaDoc checkinstallFile = null;
65     File JavaDoc postinstallFile = null;
66     File JavaDoc postremoveFile = null;
67     File JavaDoc prototypeFile = null;
68
69     // Location of the generated JNLP package information files.
70
private static String JavaDoc pkgInfoFileLocation = null;
71
72     // Temporarily created directory for JNLP resource files.
73
private static String JavaDoc tmpResourcePath = null;
74
75     // Temporarily created directory for copyright/license files.
76
private static String JavaDoc tmpCopyrightPath = null;
77     
78     /**
79      * Runs a shell command and return the output string.
80      */

81     private String JavaDoc runShellCommand(String JavaDoc[] commandStrs) {
82         BufferedReader JavaDoc br = null;
83         try {
84             Process JavaDoc proc = Runtime.getRuntime().exec(commandStrs);
85
86             // Output messages.
87
br = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(proc.getInputStream()));
88             String JavaDoc oneLine = null;
89             if ((oneLine = br.readLine()) != null) {
90                 // ONLY return the first line of the output.
91
return oneLine;
92             } else {
93                 return null;
94             }
95         } catch (IOException JavaDoc e) {
96             // If there are exceptions, just return null.
97
return null;
98         } finally {
99             // No matter what happens, always close streams already opened.
100
if (br != null) {
101                 try {
102                     br.close();
103                 } catch (IOException JavaDoc e) {
104                 }
105             }
106         }
107     }
108
109     /**
110      * Creates a new file with the given absolute file path. If it already exists,
111      * remove it first.
112      */

113     private void createFileIgnoreExistance(File JavaDoc absFilePath) throws IOException JavaDoc {
114         // Check if the given file already exists. If it is, delete it first.
115
if (absFilePath.exists()) {
116             boolean deleteSucceed = absFilePath.delete();
117             if (!deleteSucceed) {
118                 throw new IOException JavaDoc("Failed to remove already existed file: " + absFilePath);
119             }
120         }
121         
122         boolean createSucceed = absFilePath.createNewFile();
123         if (!createSucceed) {
124             throw new IOException JavaDoc("Failed to create new file: " + absFilePath);
125         }
126     }
127
128     /**
129      * Deletes the temporarily created files and directories.
130      */

131     private void deleteTempFiles() throws IOException JavaDoc {
132         if (pkginfoFile != null) pkginfoFile.delete();
133         if (checkinstallFile != null) checkinstallFile.delete();
134         if (postinstallFile != null) postinstallFile.delete();
135         if (postremoveFile != null) postremoveFile.delete();
136         if (prototypeFile != null) prototypeFile.delete();
137         if (tmpResourcePath != null) {
138             FileOperUtility.deleteDirTree(new File JavaDoc(tmpResourcePath));
139         }
140
141         if (tmpCopyrightPath != null) {
142             FileOperUtility.deleteDirTree(new File JavaDoc(tmpCopyrightPath));
143         }
144     }
145
146     /*
147      * Creates pkginfo file by extract the info fields in English locale.
148      */

149     private void createFilePkginfo(JnlpPackageInfo pkgInfo) throws IOException JavaDoc {
150         String JavaDoc packageName = pkgInfo.getPackageName();
151         String JavaDoc vendor = pkgInfo.getLocalizedJnlpInfo("en", JnlpConstants.JNLP_FIELD_VENDOR);
152         String JavaDoc productInfo = pkgInfo.getLocalizedJnlpInfo("en", JnlpConstants.JNLP_FIELD_TITLE);
153         String JavaDoc installationPath = pkgInfo.getUniqueTmpDirPath();
154         String JavaDoc versionNum = pkgInfo.getVersion();
155         String JavaDoc releaseNum = pkgInfo.getRelease();
156         // Version string is like: 1.0,RELEASE 1
157
String JavaDoc versionStr = versionNum + ",RELEASE " + releaseNum;
158
159         // Create file pkginfo. If it already exists, delete it first.
160
pkginfoFile = new File JavaDoc(pkgInfoFileLocation, FILE_NAME_PKGINFO);
161         createFileIgnoreExistance(pkginfoFile);
162         
163         // Write the package information into the pkginfo file:
164
BufferedWriter JavaDoc mBufferWriter = null;
165         try {
166             mBufferWriter = new BufferedWriter JavaDoc(new FileWriter JavaDoc(pkginfoFile));
167
168             mBufferWriter.write("PKG=" + packageName + "\n");
169             mBufferWriter.write("NAME=" + productInfo + "\n");
170             mBufferWriter.write("ARCH=sparc" + "\n");
171             mBufferWriter.write("CATEGORY=system" + "\n");
172             mBufferWriter.write("VERSION=" + versionStr + "\n");
173             mBufferWriter.write("BASEDIR=" + installationPath + "\n");
174             mBufferWriter.write("PSTAMP=" + vendor + "\n");
175             mBufferWriter.write("CLASSES=none" + "\n");
176         } catch (IOException JavaDoc e) {
177             throw new IOException JavaDoc ("Failed to write into file: " + pkginfoFile);
178         } finally {
179             // No matter what happens, always close streams already opened.
180
if (mBufferWriter != null) {
181                 try {
182                     mBufferWriter.close();
183                 } catch (IOException JavaDoc e) {
184                 }
185             }
186         }
187     }
188
189     /*
190      * Creates checkinstall file.
191      */

192     private void createFileCheckinstall() throws IOException JavaDoc {
193         // If it already exists, delete it first.
194
checkinstallFile = new File JavaDoc(pkgInfoFileLocation, FILE_NAME_CHECKINSTALL);
195         createFileIgnoreExistance(checkinstallFile);
196
197         // Write the Javaws version check script into the postremove file:
198
BufferedWriter JavaDoc mBufferWriter = null;
199         try {
200             mBufferWriter = new BufferedWriter JavaDoc(new FileWriter JavaDoc(checkinstallFile));
201
202             String JavaDoc[] javawsCheckScript = JnlpUtility.javawsCheckScript();
203             for (int lineNum = 0; lineNum < javawsCheckScript.length; lineNum++ ) {
204                 mBufferWriter.write(javawsCheckScript[lineNum] + "\n");
205             }
206
207             /* Append below lines, which are only needed on Solaris, not on Linux:
208                   cat >$1 <<EOB
209                   JAVAWS_PATH=${JAVAWS_PATH}
210                   EOB
211                   exit 0
212              */

213             mBufferWriter.write("cat >$1 <<EOB" + "\n");
214             mBufferWriter.write("JAVAWS_PATH=${JAVAWS_PATH}" + "\n");
215             mBufferWriter.write("EOB" + "\n");
216             mBufferWriter.write("exit 0" + "\n");
217         } catch (IOException JavaDoc e) {
218             throw new IOException JavaDoc ("Failed to write info into file: " + checkinstallFile);
219         } finally {
220             // No matter what happens, always close streams already opened.
221
if (mBufferWriter != null) {
222                 try {
223                     mBufferWriter.close();
224                 } catch (IOException JavaDoc e) {
225                 }
226             }
227         }
228     }
229     
230     /*
231      * Creates postinstall file.
232      */

233     private void createFilePostinstall(JnlpPackageInfo pkgInfo) throws IOException JavaDoc {
234         String JavaDoc installationPath = pkgInfo.getUniqueTmpDirPath();
235         String JavaDoc jnlpFileName = pkgInfo.getJnlpFileName();
236         boolean shortcutEnabled = pkgInfo.getShortcutEnabled();
237         boolean associationEnabled = pkgInfo.getAssociationEnabled();
238         boolean sysCacheEnabled = pkgInfo.getSystemCacheEnabled() ;
239         
240         // Create file postinstall. If it already exists, delete it first.
241
postinstallFile = new File JavaDoc(pkgInfoFileLocation, FILE_NAME_POSTINSTALL);
242         createFileIgnoreExistance(postinstallFile);
243         
244         // Write the below info into the postinstall file:
245
// # Launch javaws using below command:
246
// $JAVAWS_PATH -silent -system -import -codebase <codebase> <local location of jnlp file>
247
// exit 0
248
BufferedWriter JavaDoc mBufferWriter = null;
249         try {
250             mBufferWriter = new BufferedWriter JavaDoc(new FileWriter JavaDoc(postinstallFile));
251
252             mBufferWriter.write("# Launch javaws using below command:" + "\n");
253             mBufferWriter.write("\n");
254             
255             // Convert the installation path to url style, which will be used by javaws as codebase.
256
File JavaDoc instFile = new File JavaDoc(installationPath);
257             URL JavaDoc codebase = instFile.toURL();
258
259             String JavaDoc jnlpFileLocation = null;
260             if (installationPath.endsWith(File.separator)) {
261                 jnlpFileLocation = installationPath + jnlpFileName;
262             } else {
263                 jnlpFileLocation = installationPath + File.separator + jnlpFileName;
264             }
265
266             String JavaDoc javawsCommand = "$JAVAWS_PATH ";
267             if (sysCacheEnabled) {
268                 javawsCommand += "-system ";
269             }
270             javawsCommand += "-silent ";
271             if (shortcutEnabled) {
272                 javawsCommand += "-shortcut ";
273             }
274             if (associationEnabled) {
275                 javawsCommand += "-association ";
276             }
277             javawsCommand += "-import -codebase " + codebase + " " + jnlpFileLocation + "\n";
278            
279             mBufferWriter.write(javawsCommand);
280             
281             mBufferWriter.write("\n");
282             mBufferWriter.write("exit 0" + "\n");
283         } catch (MalformedURLException JavaDoc e) {
284             throw new IOException JavaDoc ("Failed to convert the installation path to URL style: " + installationPath);
285         } catch (IOException JavaDoc e) {
286             throw new IOException JavaDoc ("Failed to write info into file: " + postinstallFile);
287         } finally {
288             // No matter what happens, always close streams already opened.
289
if (mBufferWriter != null) {
290                 try {
291                     mBufferWriter.close();
292                 } catch (IOException JavaDoc e) {
293                 }
294             }
295         }
296     }
297
298     /*
299      * Creates postremove file.
300      */

301     private void createFilePostremove(JnlpPackageInfo pkgInfo) throws IOException JavaDoc {
302         String JavaDoc installationPath = pkgInfo.getUniqueTmpDirPath();
303         String JavaDoc jnlpFileHref = pkgInfo.getJnlpFileHref();
304         boolean sysCacheEnabled = pkgInfo.getSystemCacheEnabled();
305         
306         // Create file postremove. If it already exists, delete it first.
307
postremoveFile = new File JavaDoc(pkgInfoFileLocation, FILE_NAME_POSTREMOVE);
308         createFileIgnoreExistance(postremoveFile);
309         
310         // Write the below info into the postremove file:
311
// # Launch javaws using below command:
312
// $JAVAWS_PATH -silent -system -uninstall <jnlp_href>
313
// exit 0
314
BufferedWriter JavaDoc mBufferWriter = null;
315         try {
316             mBufferWriter = new BufferedWriter JavaDoc(new FileWriter JavaDoc(postremoveFile));
317
318             mBufferWriter.write("# Launch javaws using below command:" + "\n");
319             mBufferWriter.write("\n");
320
321             String JavaDoc javawsCommand = "$JAVAWS_PATH ";
322             if (sysCacheEnabled) {
323                 javawsCommand += "-system ";
324             }
325             javawsCommand += "-silent -uninstall " + jnlpFileHref + "\n";
326             mBufferWriter.write(javawsCommand);
327
328             // Delete the created installation path only if it's empty.
329
mBufferWriter.write("rmdir $BASEDIR > /dev/null 2>&1" + "\n");
330
331             mBufferWriter.write("\n");
332             mBufferWriter.write("exit 0" + "\n");
333         } catch (IOException JavaDoc e) {
334             throw new IOException JavaDoc ("Failed to write info into file: " + postremoveFile);
335         } finally {
336             // No matter what happens, always close streams already opened.
337
if (mBufferWriter != null) {
338                 try {
339                     mBufferWriter.close();
340                 } catch (IOException JavaDoc e) {
341                 }
342             }
343         }
344     }
345     
346     /*
347      * Generates file prototype according to the extracted local files,
348      * generated information files.
349      */

350     private void genFilePrototype(JnlpPackageInfo pkgInfo) throws IOException JavaDoc {
351         // Check if the default location for the package information files is writable.
352
File JavaDoc locationFile = new File JavaDoc(pkgInfoFileLocation);
353         if (!FileOperUtility.isDirectoryWritable(locationFile)) {
354             throw new IOException JavaDoc("The default location for the package information files "
355                 + "is not writable: " + pkgInfoFileLocation);
356         }
357         
358         // The given JNLP resource path may contain files other than the jnlp resource files,
359
// copy the required resource paths to a temp directory before packaging them.
360
File JavaDoc tmpResourcePathFile = new File JavaDoc(pkgInfoFileLocation, "." + pkgInfo.getPackageName() + ".");
361         tmpResourcePath = tmpResourcePathFile.toString();
362         FileOperUtility.copyLocalFile(pkgInfo.getResourceDirPath(), tmpResourcePath);
363
364         String JavaDoc licenseFilePath = pkgInfo.getLicenseDirPath ();
365         if (licenseFilePath != null) {
366             // Copy the copyright files into a temp copyright path, since the license file path
367
// may contains files other than the license files.If no specified license path, return.
368
File JavaDoc tmpCopyrightPathFile = new File JavaDoc(pkgInfoFileLocation, "." + FILE_NAME_COPYRIGHT + ".");
369             tmpCopyrightPath = tmpCopyrightPathFile.toString();
370             FileOperUtility.copyLocalFile(pkgInfo.getLicenseDirPath(), tmpCopyrightPath);
371         }
372
373         // Create information files.
374
try {
375             createFilePkginfo(pkgInfo);
376             createFileCheckinstall();
377             createFilePostinstall(pkgInfo);
378             createFilePostremove(pkgInfo);
379         } catch (IOException JavaDoc e) {
380             throw new IOException JavaDoc(e.getMessage());
381         }
382         
383         // Create prototype file using the JNLP files, and the generated information files.
384
// Check if file prototype doesn't exist.
385
prototypeFile = new File JavaDoc(pkgInfoFileLocation, FILE_NAME_PROTOTYPE);
386         if (prototypeFile.exists()) {
387             boolean deleteSucceed = prototypeFile.delete();
388             if (!deleteSucceed) {
389                 throw new IOException JavaDoc("Failed to remove the already existed prototype file at: " + prototypeFile);
390             }
391         }
392         
393         // Include the jnlp resource files.
394
String JavaDoc genCommand = "pkgproto " + tmpResourcePath + "= ";
395
396         // If the user specified license file, store the file in the local directory:
397
// JnlpConstants.LICENSE_INSTALLATION_BASE_PATH/<vendor>/<name>/<version>.<release>
398
if (pkgInfo.getLicenseDirPath() != null) {
399             genCommand += tmpCopyrightPath + "=" + "/usr/share/lib/javaws";
400         }
401         genCommand += " > " + prototypeFile.toString();
402
403         // Generate file prototype using pkgproto.
404
Process JavaDoc proc = null;
405         try {
406             // "/bin/sh -c" is required to interpret shell metacharacters like '>'.
407
proc = Runtime.getRuntime().exec(new String JavaDoc[] {"/bin/sh", "-c", genCommand});
408             
409             try {
410                 proc.waitFor();
411             } catch (InterruptedException JavaDoc ie) {
412                 return;
413             }
414             
415         } catch (IOException JavaDoc e) {
416             throw new IOException JavaDoc("Failed to generate prototype file using command: " +
417                     genCommand);
418         }
419
420         // Up to now, the prototype file is created successfully, but the current user and group
421
// ownerships are used for each entry in the file. But the package might not work when
422
// installed if owned by another user.
423
// Refer to http://www.sunfreeware.com/pkgadd.html for more info.
424
// So, change the ownship of the entries to bin(user) and bin(group). To be exactly,
425
// change the "userid groupid" in the prototype file to "bin bin".
426
try {
427             String JavaDoc[] getUserCommandStrs = {
428                 "/bin/sh", "-c", "echo `id` | awk -F\\( '{print $2}' | awk -F\\) '{print $1}'"};
429             String JavaDoc user = runShellCommand(getUserCommandStrs);
430             if (user != null) {
431                 String JavaDoc[] getGroupCommandStrs = {
432                     "/bin/sh", "-c", "echo `id` | awk -F\\( '{print $3}' | awk -F\\) '{print $1}'"};
433                 String JavaDoc group = runShellCommand(getGroupCommandStrs);
434                 
435                 if (group != null) {
436                     // Create a temp prototype file. If it already exists, delete it.
437
File JavaDoc tempPrototypeFile = new File JavaDoc(prototypeFile.toString() + ".tmp");
438                     tempPrototypeFile.delete();
439                     
440                     String JavaDoc[] changePrototypeCommandStrs = {
441                         "/bin/sh", "-c", "sed 's/" + user + " " + group + "/bin bin/' " + prototypeFile.toString() + " > "
442                         + tempPrototypeFile.toString()};
443
444                     Runtime.getRuntime().exec(changePrototypeCommandStrs);
445                     try {
446                         proc.waitFor();
447                     } catch (InterruptedException JavaDoc ie) {
448                         return;
449                     }
450                     
451                     if (tempPrototypeFile.exists()) {
452                         // The temp file is created successfully. Delete the original prototype file
453
// and rename the generated temp file to prototype.
454
prototypeFile.delete();
455                         tempPrototypeFile.renameTo(prototypeFile);
456                     }
457                 }
458             }
459         } catch (IOException JavaDoc e) {
460             // If the above ownships changing operation fails, just ignore it.
461
}
462         
463         // Edit prototype file to add information file entries.
464
// First check if prototype is created successfully.
465
if (!prototypeFile.exists()) {
466             throw new IOException JavaDoc("No generated prototype file for generating the installable package.");
467         }
468         
469         // *Append* information file items into prototype file:
470
BufferedWriter JavaDoc mBufferWriter = null;
471         try {
472             mBufferWriter = new BufferedWriter JavaDoc(new FileWriter JavaDoc(prototypeFile, true));
473
474             mBufferWriter.write("i pkginfo=" + pkginfoFile.toString() + "\n");
475             String JavaDoc inputLicensePath = pkgInfo.getLicenseDirPath();
476             if (inputLicensePath != null) {
477                 copyrightFile = new File JavaDoc(inputLicensePath, "LICENSE.en");
478                 mBufferWriter.write("i copyright=" + copyrightFile.toString() + "\n");
479             }
480             mBufferWriter.write("i checkinstall=" + checkinstallFile.toString() + "\n");
481             mBufferWriter.write("i postinstall=" + postinstallFile.toString() + "\n");
482             mBufferWriter.write("i postremove=" + postremoveFile.toString() + "\n");
483         } catch (IOException JavaDoc e) {
484             throw new IOException JavaDoc ("Failed to write installation file entries to : " + prototypeFile);
485         } finally {
486             // No matter what happens, always close streams already opened.
487
if (mBufferWriter != null) {
488                 try {
489                     mBufferWriter.close();
490                 } catch (IOException JavaDoc e) {
491                 }
492             }
493         }
494     }
495
496     /*
497      * Generates the package according to the generated prototype, extracted local
498      * temp files, and generated information files.
499      */

500     public void generatePackage(JnlpPackageInfo pkgInfo) throws IOException JavaDoc {
501         // If the package destination path is not specified, use the default spool
502
// directory (/var/spool/pkg).
503
String JavaDoc packagePath = pkgInfo.getOutputDirPath();
504         if (packagePath == null) {
505             packagePath = "/var/spool/pkg";
506         }
507         File JavaDoc destinationPath = new File JavaDoc(packagePath);
508         if (!FileOperUtility.isDirectoryWritable(destinationPath)) {
509             throw new IOException JavaDoc("The destination directory for the generated package "
510                 + "is not writable: " + destinationPath);
511         }
512
513         // Use the resource path as the location of the generated package information files.
514
String JavaDoc resourcePath = pkgInfo.getResourceDirPath();
515         pkgInfoFileLocation = resourcePath;
516         
517         // Generate the prototype file.
518
try {
519             genFilePrototype(pkgInfo);
520         } catch (IOException JavaDoc e) {
521             // On failure, try to delete all the created files and directories.
522
deleteTempFiles();
523
524             throw new IOException JavaDoc(e.getMessage());
525          }
526         
527         // Generate the package using command:
528
// pkgmk -o -f <prototype file path> -d packagePath
529
String JavaDoc genCommand = "pkgmk -o -f " + prototypeFile.toString() + " -d " + packagePath;
530         BufferedReader JavaDoc br = null;
531         try {
532             Process JavaDoc proc = Runtime.getRuntime().exec(genCommand);
533
534             // Output messages.
535
br = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(proc.getInputStream()));
536             String JavaDoc oneLine = null;
537             System.out.println("--- Output messages while generating the PKG package ---");
538             while ((oneLine = br.readLine()) != null) {
539                 System.out.println(oneLine);
540             }
541
542             // Error messages.
543
br = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(proc.getErrorStream()));
544             while ((oneLine = br.readLine()) != null) {
545                 System.out.println(oneLine);
546             }
547         } catch (IOException JavaDoc e) {
548             throw new IOException JavaDoc("Failed to generate an installable package using command: "
549                 + genCommand);
550         } finally {
551             // No matter what happens, always close streams already opened.
552
if (br != null) {
553                 try {
554                     br.close();
555                 } catch (IOException JavaDoc e) {
556                 }
557             }
558
559             // No matter what happens, always try to delete all the created temp files.
560
deleteTempFiles();
561         }
562     }
563 }
564
Popular Tags