KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > Original-Extractor


1 /* $Id$
2  *
3  * Copyright (c) 1998 Xenonsoft Limited. All Rights Reserved.
4  *
5  * This software is protected by copyright. You are hereby notified from
6  * now by reading this message. This software is also the confidential
7  * and proprietary information of Xenonsoft Limited. ("Confidential
8  * Information").
9  *
10  * This software is distributed under the Xenonsoft Public end user
11  * License ("XPeL"), where the machine-readable source code is provided
12  * under the "Open Source" model.
13  * For more information, please read the file "LICENSE-XPL.txt"
14  *
15  * FIXME:
16  */

17
18
19 // Extractor.java
20

21 // Description:
22
// A class to extract the package file into the current directory.
23
//
24
// Author:
25
// Peter Pilgrim
26
// Wed Feb 10 05:51:34 GMT 1999
27
//
28
// RCS HEADER
29
//
30
// $Author$
31
// $Date$
32
// $Source$
33
// $Revision$ $State$ $Locker$
34
//
35
// History
36
// ================================================================================
37
// $Log$
38

39
40 import java.io.*; // for File I/O Classes
41
import java.net.*; // Network/Internet classes
42
import java.util.*; // for Vector/Hashtable
43
import java.util.zip.*; // for ZipInputStream/ZipEntry classes
44
import java.lang.reflect.*; // for Method, Constructor et al
45

46 /*
47  * Extractor is a self extracting java class that can package a software
48  * distribution.
49  *
50  * The extractor class works by searching for a special key line
51  * in the class file, which it opens. The special key line is appended
52  * to the end of the class file and the distribution zip or jar file
53  * is append afterwards.
54  *
55  * The key line consist of the key value, the archive type,
56  * and the name of the java class that contains the
57  * <I> `public static void main( String [] )'</I>.
58  *
59  * The key line is:
60  * <PRE>
61  *
62  * KEY_LINE:
63  * KEY_VALUE '|' ARCHIVE_TYPE '|' CLASS_NAME '|' EXTRA_CLASS_PATH '|' '\n' ;
64  *
65  * Where:
66  * KEY_VALUE: "-=+ARCHIVE+=-" ;
67  *
68  * ARCHIVE_TYPE: currently only "ZIP" is recognised
69  * and case insensitive;
70  *
71  * CLASS_NAME: the java software package name ;
72  * ( e.g. "xenon.free.install.FreeInstaller" )
73  *
74  * EXTRA_CLASS_PATH: the extra class path to prepend to the
75  * default class path. For example to specify a
76  * pathname to JAR and/or ZIP files.
77  *
78  * </PRE>
79  *
80  * @see runApplicationInstaller
81  * @author Peter Pilgrim
82  * @version 1.0 , Mon Feb 22 23:07:13 GMT 1999
83  */

84 public class Extractor {
85
86     public final static boolean debug=true;
87     
88     /** List of extracted files so we can do a cleanup on exit */
89     protected Vector extractedFiles;
90     /** The archive type */
91     protected String JavaDoc archiveType="";
92     /** The class name to load dynamically and to call `main()' */
93     protected String JavaDoc appClassName="";
94     /** The extra class path */
95     protected String JavaDoc extraClassPath="";
96     
97     // A static reference to make sure extracted files are clean up at
98
// exit using the finalizer
99
private static Extractor extractor= null;
100     
101
102     /** Creates the archive class */
103     public Extractor()
104     {
105     extractedFiles = new Vector(128);
106     }
107
108
109     
110     /** used to print debuggable output */
111     private static void printChar( int ch )
112     {
113     if ( ch >= 32 && ch < 128 )
114         System.out.print((char)ch);
115     else
116         System.out.print("<"+ch+">");
117     }
118     
119     /** searches the entire input stream for a special key
120      * Once the special key is recognized, the functions stops reading
121      * @param pis the input stream
122      * @param key the special key string to search for
123      *
124      * @return true if the key was found.
125      */

126     private boolean findArchiveKey( PushbackInputStream pis, String JavaDoc key )
127     throws IOException
128     {
129     boolean archiveKeyFound=false;
130     int ch=0;
131
132     while ( ch != -1 ) {
133         int count=0;
134         for (int k=0; k<key.length(); ++k) {
135         ch = pis.read();
136         // printChar(ch); // DEBUG:
137
if ( ch == key.charAt(k))
138             ++count;
139         else
140             break;
141         }
142         if (count == key.length() ) {
143         archiveKeyFound = true;
144         break;
145         }
146         
147     }
148
149     return (archiveKeyFound );
150     }
151
152     /** reads the delimiter bar character ('|') from the input stream
153      * @param pis the input stream
154      */

155     private void readDelimiter( PushbackInputStream pis )
156     throws IOException
157     {
158     if (debug) System.out.println("readDelimiter");
159
160     int ch = pis.read();
161     // printChar(ch); // DEBUG:
162
if ( ch != '|' )
163         throw new IOException( "delimiter expected." );
164     }
165     
166     
167     /** reads the archive type from the input stream
168      * @param pis the input stream
169      *
170      * @return the archive type string
171      */

172     private String JavaDoc readArchiveType( PushbackInputStream pis )
173     throws IOException
174     {
175     if (debug) System.out.println("readArchiveType");
176
177     StringBuffer JavaDoc type = new StringBuffer JavaDoc(8);
178     int ch=0;
179
180     int counter=0;
181     do {
182         ch = pis.read(); ++counter;
183            // printChar(ch); // DEBUG:
184
if ( ch != '|' )
185         type.append((char)ch);
186         else
187         pis.unread(ch);
188     }
189     while ( ch != '|' && counter < 32 );
190
191     return type.toString();
192     }
193     
194     /** Reads the application class name from the input stream
195      * @param pis the input stream
196      *
197      * @return the application class name string
198      */

199     private String JavaDoc readApplicationClass( PushbackInputStream pis )
200     throws IOException
201     {
202     if (debug) System.out.println("readApplicationClass");
203     
204     StringBuffer JavaDoc appClass = new StringBuffer JavaDoc(8);
205     int ch=0;
206
207     int counter=0;
208     do {
209         ch = pis.read(); ++counter;
210         // printChar(ch); // DEBUG:
211
if ( ch != '|' )
212         appClass.append((char)ch);
213         else
214         pis.unread(ch);
215     }
216     while ( ch != '|' && counter < 512 );
217
218     return appClass.toString();
219     }
220
221     /** Reads the extra class path string
222      * @param pis the input stream
223      *
224      * @return the extra class path string
225      */

226     private String JavaDoc readExtraClassPath( PushbackInputStream pis )
227     throws IOException
228     {
229     if (debug) System.out.println("readExtraClassPath");
230     
231     StringBuffer JavaDoc ecp = new StringBuffer JavaDoc(64);
232     int ch=0;
233
234     int counter=0;
235     do {
236         ch = pis.read(); ++counter;
237         // printChar(ch); // DEBUG:
238
if ( ch != '|' )
239         ecp.append((char)ch);
240         else
241         pis.unread(ch);
242     }
243     while ( ch != '|' && counter < 512 );
244
245     return ecp.toString();
246     }
247
248     /** Extracts a zip or jar archive from the input stream
249      * @param pis the input stream
250      *
251      */

252     private void extractZipFiles( InputStream is )
253     throws IOException
254     {
255     System.out.println( "extractZipFiles()" ); // DEBUG:
256

257     ZipInputStream zis = null;
258     try {
259         zis = new ZipInputStream( is );
260         ZipEntry zipEntry;
261         int num_entries=0;
262     
263         byte [] buffer = new byte[4096];
264         int total_bytes=0;
265         do {
266         zipEntry = zis.getNextEntry();
267         if (zipEntry != null ) {
268             ++num_entries;
269             int method = zipEntry.getMethod();
270             if (debug) {
271             System.out.println( "*** ENTRY ["+num_entries+"] ***" );
272             System.out.println( " name: " + zipEntry.getName() );
273             System.out.println( " size: " + zipEntry.getSize() );
274             System.out.println( " extra: " + zipEntry.getExtra() );
275             System.out.println( " compressed size: " + zipEntry.getCompressedSize() );
276             System.out.println( " method: " +
277                         (method == ZipEntry.DEFLATED ? "(Compressed)" :
278                          method == ZipEntry.STORED ? "(Stored)" : "UNKNOWN!" ));
279             }
280             System.out.print('.');
281         
282             String JavaDoc entryFilePath = zipEntry.getName();
283             entryFilePath = entryFilePath.replace('/', File.separatorChar );
284             entryFilePath = entryFilePath.replace('\\', File.separatorChar );
285             if (debug) System.out.println( "extracting: `"+entryFilePath +"'");
286         
287         
288             File entryFile = new File( entryFilePath );
289             if (entryFile.isDirectory() )
290             // Create directory
291
entryFile.mkdirs();
292             else {
293             // Extract the zip file entry
294
if ( entryFile.getParent() != null )
295                 new File(entryFile.getParent()).mkdirs();
296             
297             extractedFiles.addElement( entryFilePath );
298             FileOutputStream fos = new FileOutputStream( entryFile );
299             int num_bytes;
300             while ( (num_bytes = zis.read( buffer, 0, buffer.length )) >= 0 ) {
301                 fos.write( buffer, 0, num_bytes );
302                 fos.flush(); // FLUSH IT!
303
total_bytes += num_bytes;
304             }
305             fos.close(); // CLOSE IT!!
306
}
307             zis.closeEntry();
308         }
309         }
310         while (zipEntry != null);
311     
312         System.out.println("\n\nExtracted a total "+total_bytes+" byte"+
313                    (total_bytes == 1 ? "" : "s" ) +
314                    " from "+ num_entries+" ent"+
315                    (num_entries == 1 ? "ry" : "ries" )+"." );
316     }
317     finally {
318         // Use the finally clause to make sure the stream is closed.
319
if (zis != null) try { zis.close(); } catch (IOException e ) { ; }
320     }
321     }
322
323     /** function to self extract the files from the extractor class
324      * @param pis the input stream
325      *
326      */

327     public void extractFiles()
328     {
329     String JavaDoc resname = getClass().getName();
330
331     String JavaDoc left ="-=+";
332     String JavaDoc right="+=-";
333     String JavaDoc key=left+"ARCHIVE"+right;
334     
335     PushbackInputStream pis=null;
336     try {
337         pis = new PushbackInputStream(
338         new FileInputStream( resname+".class" ));
339         int ch=0;
340         boolean foundKey=false;
341         //
342
// Search for the key in the stream if available.
343
//
344
foundKey = findArchiveKey( pis, key );
345         if ( foundKey ) {
346         if (debug) System.out.println("\n*** KEY FOUND !!! ***");
347         readDelimiter(pis);
348         archiveType = readArchiveType(pis);
349         readDelimiter(pis);
350         appClassName = readApplicationClass(pis);
351         readDelimiter(pis);
352         extraClassPath = readExtraClassPath(pis);
353         readDelimiter(pis);
354         
355         // Handle the case of extra new line character usually
356
// caused with the shell /bin/echo.
357
ch = pis.read();
358         if ( ch != '\n' ) {
359             pis.unread(ch);
360         }
361         
362         if (debug) {
363             System.out.println( "\n***DEBUG***" );
364             System.out.println( "archiveType=`"+archiveType+"'");
365             System.out.println( "appClassName=`"+appClassName+"'" );
366             System.out.println( "extraClassPath=`"+extraClassPath+"'");
367         }
368         
369         if ( archiveType.equalsIgnoreCase("ZIP" ) )
370             extractZipFiles( pis );
371         else
372             throw new RuntimeException JavaDoc("Cannot extract archive type:`"+archiveType+"'");
373         }
374         else
375         throw new RuntimeException JavaDoc("Cannot find archive key.");
376
377     }
378     catch (IOException ioe) {
379         System.err.println(ioe);
380         cleanupFiles();
381         System.exit(1);
382     }
383     finally {
384         if (pis != null) try { pis.close(); } catch (IOException e) { ; }
385     }
386     
387     }
388     
389     /** Beginning self extracting process and runs the
390      * installer application class installer.
391      *
392      * Assuming self extraction of the application class worked, the
393      * application class is loaded dynamically and this class must
394      * have an method `public static void main ( String [] )' to work
395      * sucessfully. This main method of the application is then called
396      * and we are away. This method may not return.
397      *
398      * @param args an array of arguments to pass to the application
399      * class.
400      */

401     public void runApplicationInstaller( String JavaDoc [] args )
402     {
403     System.out.println( getClass().getName()+".runInstaller()" ); // DEBUG:
404

405     extractFiles();
406     
407     if ( extraClassPath.length() > 0 ) {
408         //
409
// Modify the class path take into account
410
// the difference between Window NT and UNIX systems
411
//
412
String JavaDoc pathSep = System.getProperty("path.separator");
413         String JavaDoc fileSep = System.getProperty("file.separator");
414
415         if ( pathSep.equals(";") )
416         // Win NT replace (:) with (;)
417
extraClassPath = extraClassPath.replace( ':' , ';' );
418         else
419         // UNIX replace (;) with (:)
420
extraClassPath = extraClassPath.replace( ';' , ':' );
421         
422         if ( fileSep.equals("\\") )
423         // Win NT replace (/) with (\)
424
extraClassPath = extraClassPath.replace( '/' , '\\' );
425         else
426         // UNIX replace (\) with (/)
427
extraClassPath = extraClassPath.replace( '\\' , '/' );
428
429         // Properties propsJVM = System.getProperties();
430
// String ncp = extraClassPath + pathSep +
431
// System.getProperty("java.class.path");
432
// propsJVM.put("java.class.path",ncp);
433

434         if (debug)
435             System.out.println("*NEW* classpath = "+ System.getProperty("java.class.path") );
436     }
437     
438     //
439
// Now try to load the Extractor's invocation class,
440
// which must have a `public static void main( String [] )' method.
441
// Notice I am using reflection to do this.
442
//
443
try {
444         String JavaDoc [] dummyArgs = new String JavaDoc [0];
445         Class JavaDoc mainClass = Class.forName( appClassName );
446         // Class mainClass = this.loadClass( appClassName );
447
System.out.println( "loaded class:"+appClassName ); // DEBUG:
448
Method mainMethod = mainClass.getMethod(
449         "main", new Class JavaDoc [] { dummyArgs.getClass() } );
450         System.out.println( "about to invoke public static void main( String [] )"); // DEBUG:
451
Object JavaDoc retVal = mainMethod.invoke( null, new Object JavaDoc [] { args } );
452         // *PP* Of course the `retVal' is a java.lang.reflect.Void object.
453
}
454     catch ( ClassNotFoundException JavaDoc e1 ) {
455         System.err.println(e1);
456     }
457     catch ( NoSuchMethodException JavaDoc e2 ) {
458         System.err.println(e2);
459     }
460     catch ( IllegalAccessException JavaDoc e3 ) {
461         System.err.println(e3);
462     }
463     catch ( InvocationTargetException e4 ) {
464         System.err.println("Method threw an "+e4.getTargetException() );
465     }
466     
467     }
468
469     /** A method to clean up extracted automatically */
470     public void cleanupFiles()
471     {
472     System.out.println("========================================");
473     System.out.println("cleaning up files ");
474     Enumeration etor = extractedFiles.elements();
475     while ( etor.hasMoreElements() ) {
476         String JavaDoc filename = (String JavaDoc)etor.nextElement();
477         File file = new File( filename );
478         if ( file.exists() ) {
479         if (debug) System.out.println("deleting file:`"+filename+"'" );
480         file.delete();
481         }
482     }
483     extractedFiles.removeAllElements();
484     }
485
486     /** the defensive and safe to make sure extracted files are
487      * cleaned up at exit.
488      *
489      * @exception alway define finalize throw <B>THROWABLE</B>
490      */

491     public void finalize()
492     throws Throwable JavaDoc
493     {
494     try {
495         if (debug) System.out.println("Extractor.finalize()");
496         cleanupFiles();
497     }
498     finally {
499         super.finalize(); // ALWAYS chain finalize methods
500
}
501     }
502     
503     public static void main( String JavaDoc [] args )
504     {
505     // We are using deprecated 1.2 method is anycase to get the extractor
506
// to clean up files and exits.
507
//
508
// ----------------------------------------------------------------------
509
// System.runFinalizersOnExit( boolean flag )
510
// DEPRACATED. This method is inherently unsafe. It may result in
511
// finalizers being called on live objects, resulting in erratic
512
// behaviour or deadlock
513
// ----------------------------------------------------------------------
514
//
515
// I don't know why Sun would introduce a new API feature 1.1 and then
516
// immediately depracate it in Java 1.2 Beta 4. Clearly it says that
517
// that the JVM currently available are unsafe, because the Java
518
// language is incomplete requires a new keyword to distinguish
519
// between finalizing objects and those that are not, and a concept
520
// to schedule the finalization within threads.
521
// *PP* Peter Pilgrim Mon Feb 22 22:35:37 GMT 1999
522
System.runFinalizersOnExit(true);
523
524     extractor = new Extractor();
525     extractor.runApplicationInstaller( args );
526     }
527     
528     // private void extractIndirectlyZipFiles( InputStream is )
529
// throws IOException
530
// {
531
// System.out.println( "extractIndirectlyZipFiles()" ); // DEBUG:
532
//
533
// File tempZipFile = new File("package.zip");
534
// if (tempZipFile.exists() )
535
// tempZipFile.delete();
536
//
537
// //
538
// // Extract a zip file (INDIRECTLY) by copying the rest of the
539
// // input stream to a temporary file.
540
// //
541
// extractedFiles.addElement(tempZipFile);
542
// byte [] buffer = new byte[1024];
543
// int bytes_read;
544
// FileOutputStream fos = new FileOutputStream( tempZipFile );
545
// while ( ( bytes_read = is.read( buffer )) >= 0 )
546
// fos.write( buffer, 0, bytes_read );
547
// fos.close();
548
//
549
// ZipFile zipFile = new ZipFile( tempZipFile );
550
// ZipInputStream zis = null;
551
// ZipEntry zipEntry;
552
// int total=0;
553
//
554
// Enumeration etor = zipFile.entries();
555
// while ( etor.hasMoreElements() ) {
556
// zipEntry = (ZipEntry)etor.nextElement();
557
// ++total;
558
// int method = zipEntry.getMethod();
559
// System.out.println( "*** ENTRY ["+total+"] ***" );
560
// System.out.println( "name: " + zipEntry.getName() );
561
// System.out.println( "size: " + zipEntry.getSize() );
562
// System.out.println( "extra: " + zipEntry.getExtra() );
563
// System.out.println( "compressed size: " + zipEntry.getCompressedSize() );
564
// System.out.println( "method: " +
565
// (method == ZipEntry.DEFLATED ? "(Compressed)" :
566
// method == ZipEntry.STORED ? "(Stored)" : "PASS!" ));
567
// }
568
// }
569
}
570
571 // fini
572
Popular Tags