KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > 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.*; // for 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 /**
48  * Extractor is a self extracting java class that can package a software
49  * distribution.
50  * <P>
51  * The extractor class works by searching for a special key line
52  * in the class file, which it opens. The special key line is appended
53  * to the end of the class file and the distribution zip or jar file
54  * is append afterwards.
55  * <P>
56  * The key line consist of the key value, the archive type,
57  * and the name of the java class that contains the
58  * <I> `public static void main( String [] )'</I>.
59  * <P>
60  * The key line is:
61  *
62  * <PRE>
63  *
64  * KEY_LINE:
65  * KEY_VALUE '|' ARCHIVE_TYPE '|' CLASS_NAME '|' EXTRA_CLASS_PATH '|' '\n' ;
66  *
67  * Where:
68  * KEY_VALUE: "-=+ARCHIVE+=-" ;
69  *
70  * ARCHIVE_TYPE: currently only "ZIP" is recognised
71  * and case insensitive;
72  *
73  * CLASS_NAME: the java software package name ;
74  * ( e.g. "xenon.free.install.FreeInstaller" )
75  *
76  * EXTRA_CLASS_PATH: the extra class path to prepend to the
77  * default class path. For example to specify a
78  * pathname to JAR and/or ZIP files.
79  *
80  * </PRE>
81  * <P>
82  * The extractor can retrieve resource that are stored in a ZIP or JAR file.
83  * If the application calls <code>Class.getResource()</code>. The extractor
84  * has it own URL protocol classes <code>ExtractorURLConnection</code> and
85  * <code>ExtractorURLStreamHandler</code> to open up connection to the resources.
86  * <P>
87  * The class will try to load resource if the protocol of the connection is
88  * <B>extractor</B>. It tries to emulate default <I>primordial</I> class loader
89  * by caching & encoding location of the resource in a URL specification and then
90  * retrieving the details later. This means that you can write
91  * <PRE>
92  * URL url = getClass().getClassLoader.getResource("images/dukey.gif");
93  * is = url.openConnection();
94  * </PRE>
95  * <P>
96  * You can read the data into a <code>ByteArrayOutputStream</code> and create
97  * image using the <code>Toolkit.getToolkit.createImage</code>
98  * <P>
99  * <B>NOTE: THIS CODE WORKS BUT IS INCOMPLETE!</B>
100  * Ok the theory is nice but there something not right. Extractor protocol
101  * needs more details of the internal workings of the primordial getResource()
102  * mechanism, because the present code does not work in practice.
103  * <P>
104  *
105  * <P>
106  * @see runApplicationInstaller
107  * @author Peter Pilgrim
108  * @version 1.0 , Mon Feb 22 23:07:13 GMT 1999
109  */

110 public class Extractor extends ClassLoader JavaDoc
111 implements URLStreamHandlerFactory
112 {
113     /** Compiled in constant, the debuggable flag */
114     public static boolean debug=false;
115
116     /** List of extracted files so we can do a cleanup on exit */
117     protected Vector extractedFiles;
118     /** The archive type */
119     protected String JavaDoc archiveType="";
120     /** The class name to load dynamically and to call `main()' */
121     protected String JavaDoc appClassName="";
122     /** The extra class path */
123     protected String JavaDoc extraClassPath="";
124     
125     // A static reference to make sure extracted files are clean up at
126
// exit using the finalizer
127
private static Extractor extractor= null;
128
129     /** A cache of URL extractor protocol filepath */
130     private static Hashtable filePathCache = new Hashtable();
131     
132     /** A reverse cache of URL extractor protocol filepath */
133     private static Hashtable reverseFilePathCache = new Hashtable();
134     
135     /** A cache of previous loaded classes */
136     private static Hashtable classesCache = new Hashtable();
137
138     private static boolean initialized = false;
139     
140     /** Creates the archive class */
141     public Extractor()
142     {
143     extractedFiles = new Vector(128);
144     if (!initialized) {
145         URL.setURLStreamHandlerFactory(this);
146         initialized = true;
147     }
148     }
149
150     /** Add another filepath to list of extracted URL filepath caches
151      * If the file path ends with ".zip" or the ".jar" suffix
152      * then the key will be "ZIP" otherwise it is "FILE".
153      *
154      * @param filepath the filepath as a string
155      * @return <code>String</code> the key
156      */

157     public String JavaDoc addToFilePathCache( File filePath )
158     {
159     int idx = filePathCache.size();
160     String JavaDoc absolutePath = filePath.getAbsolutePath();
161     String JavaDoc key;
162     String JavaDoc identifier;
163
164     if ( absolutePath.endsWith(".zip") || absolutePath.endsWith(".jar") )
165         identifier="ZIP";
166     else
167         identifier="FILE";
168
169     key = (String JavaDoc)reverseFilePathCache.get( absolutePath );
170     if (key != null) {
171         if (debug) System.out.println(">>>> *OLD* KEY: "+key+"\tVALUE:"+absolutePath);
172         return (key);
173     }
174     
175     key = identifier+idx;
176     if (debug) System.out.println(">>>> *NEW* KEY: "+key+"\tVALUE:"+absolutePath);
177     filePathCache.put( key, absolutePath );
178     reverseFilePathCache.put( absolutePath, key );
179     
180     return (key);
181     }
182         
183     /** A convenience method to load a class dynamically and resolve
184      * it by default
185      *
186      * @param typeName the fully qualified class name.
187      * @exception ClassNotFoundException thrown if the class cannot be loaded
188      */

189     public Class JavaDoc loadClass( String JavaDoc typeName )
190     throws ClassNotFoundException JavaDoc
191     {
192     return loadClass( typeName, true );
193     }
194     
195     /** This is one abstract method of ClassLoader that all subclasses
196      * must define. The purpose of this method is to load an array of bytes
197      * from some location and pass them to defineClass(). If the resolveIt
198      * argument is true, it must also call resolveClass(), which will be
199      * things like verify the presence of a superclass. This method may be
200      * called to load superclass that are system classes, because of this
201      * second step, and it must take this into account.
202      *
203      * @param typeName the fully qualified classname.
204      * @param resolveIt true if this class should be resolved.
205      *
206      * @exception ClassNotFoundException thrown if the class cannot be loaded
207      */

208     public synchronized Class JavaDoc loadClass(String JavaDoc typeName, boolean resolveIt)
209     throws ClassNotFoundException JavaDoc
210     {
211     // The `ClassLoader' has a built-cache of classes it has already loaded.
212
// So, first check the cache.
213
if (debug) System.out.println( "DEBUG: Extractor.loadClass("+typeName+","+resolveIt+")" );
214     Class JavaDoc result = (Class JavaDoc)classesCache.get( typeName );
215     if ( result != null ) {
216             // Return an already-loaded class with this class loader.
217
return result;
218         }
219
220         result = findLoadedClass(typeName);
221         if (result != null) {
222             // Return an already-loaded class from the primordial
223
return result;
224         }
225     
226         // Check with the primordial class loader because after this method
227
// loads a class, it will be called again to load superclasses.
228
// It is possible that these superclasses may be system classes.
229
// We have to able to load them as welll.
230
try {
231             result = super.findSystemClass(typeName);
232             // Return a system class
233
return result;
234         }
235         catch (ClassNotFoundException JavaDoc e) {
236         }
237
238         // Don't attempt to load a system file except through the
239
// primordial class loader. THIS WOULD BE A SECURITY HOLE!
240
//
241
// TODO: Java 2 Platform "javax." ?
242
if (typeName.startsWith("java.")) {
243             throw new ClassNotFoundException JavaDoc();
244         }
245
246         // Try to load it the class type extended class path.
247
byte typeData[] = getTypeFromExtendedClassPath(typeName);
248         if (typeData == null) {
249         throw new ClassNotFoundException JavaDoc();
250         }
251     else {
252     }
253
254         // Now call an inherited method to convert (parse) those bytes
255
// into a Java class type.
256
result = defineClass(typeName, typeData, 0, typeData.length);
257         if (result == null) {
258             throw new ClassFormatError JavaDoc();
259         }
260
261     // If the resolve argument is true, call the inherited
262
// resolveClass method.
263
if (resolveIt) {
264             resolveClass(result);
265         }
266
267     // Add new loaded class to the cache
268
classesCache.put( typeName, result );
269
270         // Return the dynamically loaded class type
271
return result;
272     }
273
274     /** Loads the bytes for a dynamic class or property
275      * @param typeName the type name
276      * @return the byte representing the class or null of the data
277      * could not be loaded.
278      */

279     private byte[] getTypeFromExtendedClassPath( String JavaDoc typeName )
280     {
281     if (debug) System.out.println( "DEBUG: Extractor.getTypeFromExtendedClassPath("+typeName+")" );
282     
283     byte[] typeData = null;
284     String JavaDoc fileName = typeName.replace( '.', File.separatorChar ) + ".class";
285     
286     if (debug) System.out.println("fileName="+fileName );
287     
288     String JavaDoc ecp = extraClassPath + File.pathSeparatorChar +
289         System.getProperty("java.class.path");
290     // DEBUG: System.out.println( "ecp="+ecp);
291

292     
293     StringTokenizer st =
294         new StringTokenizer( ecp, File.pathSeparatorChar+"" );
295
296     while ( st.hasMoreTokens() && typeData == null) {
297         String JavaDoc filePath = st.nextToken();
298         
299         File file = new File( filePath, fileName );
300         // System.out.println( "filePath="+filePath );
301

302         if ( filePath.endsWith(".zip") || filePath.endsWith(".jar") ) {
303         File zf = new File(filePath);
304         if ( zf.exists() ) {
305             // System.out.println("DEBUG: ZIP/JAR file:`"+filePath+"' found");
306
typeData = getTypeFromZipFile( new File(filePath), fileName );
307         }
308         }
309         else if ( file.exists() ) {
310         // System.out.println("DEBUG: Java class file:`"+fileName+"' exists!");
311
typeData = getTypeFromLocalFile( file );
312         }
313     }
314     
315     return (typeData);
316     }
317
318
319     /** Read the class type data from a filename on local
320      * filestore.
321      * @param file the file object representing the class file.
322      * @return the byte representing the class or null of the data
323      * could not be loaded.
324      */

325     private byte[] getTypeFromLocalFile( File file )
326     {
327     byte[] typeData = null;
328     BufferedInputStream bis=null;
329     ByteArrayOutputStream out = new ByteArrayOutputStream();
330
331     try {
332         byte [] buffer = new byte[8192];
333         bis = new BufferedInputStream( new FileInputStream( file ));
334         int total_bytes=0;
335         int num_bytes;
336         while ( (num_bytes = bis.read( buffer )) > 0 ) {
337         out.write( buffer, 0, num_bytes );
338         total_bytes += num_bytes;
339         }
340         typeData = out.toByteArray();
341     }
342     catch (IOException e) {
343        typeData = null;
344     }
345     finally {
346         if (bis != null) try { bis.close(); } catch (IOException e) { };
347     }
348
349     return (typeData);
350     }
351     
352     /** Read the class type data from a filename from inside a ZIP
353      * file or Java Archive if possible.
354      * @param typeFileName the type's local filename
355      * @return the byte representing the class or null of the data
356      * could not be loaded.
357      */

358     private byte[] getTypeFromZipFile( File file, String JavaDoc typeFileName )
359     {
360     byte[] typeData = null;
361     ZipFile zipFile = null;
362     
363     try {
364         zipFile = new ZipFile( file );
365         Enumeration entries = zipFile.entries();
366         while ( entries.hasMoreElements() ) {
367         ZipEntry zipEntry = (ZipEntry)entries.nextElement();
368         // System.out.println( "entry:"+zipEntry.getName() );
369

370         if (zipEntry.getName().equals( typeFileName ) ) {
371             if (debug) System.out.println( "*FOUND* zip/jar file entry:`"+typeFileName+"' ("+file.getPath()+")" );
372
373             byte [] buffer = new byte[8192];
374             BufferedInputStream bis =
375             new BufferedInputStream( zipFile.getInputStream( zipEntry ) );
376             ByteArrayOutputStream out = new ByteArrayOutputStream();
377
378             long dataSize = zipEntry.getSize();
379             int c = bis.read();
380             while (c != -1 && dataSize > 0 ) {
381             --dataSize;
382             out.write(c);
383             if (dataSize > 0) c = bis.read();
384             }
385             typeData = out.toByteArray();
386         }
387         }
388     }
389     catch (IOException e) {
390         // e.printStackTrace();
391
}
392     finally {
393         if (zipFile != null) try { zipFile.close(); } catch (IOException e) { };
394     }
395
396     return (typeData);
397     }
398
399     /** Returns an input stream to the application resource
400      * @param name the name of the resource
401      */

402     public InputStream getResourceAsStream( String JavaDoc name )
403     {
404     if (debug) System.out.println("Extractor.getResourceAsStream("+name+")" );
405
406     URL url = getResource( name );
407     InputStream is=null;
408     if ( url != null ) {
409         try {
410         is = url.openStream();
411         }
412         catch (IOException ioe) {
413         is = null;
414         }
415     }
416     
417     return (is);
418     }
419     
420     /** Returns a specific URL to the application resource
421      * @param name the name of the resource
422      */

423     public URL getResource( String JavaDoc name )
424     {
425     if (debug) System.out.println("Extractor.getResource("+name+")" );
426
427     return getCachedURLForResource( name );
428     }
429     
430     /** Searches for the application resources and returns
431      * input stream object to read data from it.
432      * @param typeName the type name
433      * @return the byte representing the class or null of the data
434      * could not be loaded.
435      */

436     private URL getCachedURLForResource( String JavaDoc fileName )
437     {
438     if (debug) System.out.println( "Extractor.getCachedURLForResource("+fileName+")" );
439
440     URL url = null;
441     
442     String JavaDoc ecp = extraClassPath + File.pathSeparatorChar +
443         System.getProperty("java.class.path");
444     // System.out.println( "ecp="+ecp);
445

446     StringTokenizer st =
447         new StringTokenizer( ecp, File.pathSeparatorChar+"" );
448
449     while ( st.hasMoreTokens() && url == null) {
450         String JavaDoc filePath = st.nextToken();
451         
452         File file = new File( filePath, fileName );
453         // System.out.println( "filePath="+filePath );
454

455         if ( filePath.endsWith(".zip") || filePath.endsWith(".jar") ) {
456         File zf = new File(filePath);
457         if ( zf.exists() ) {
458             // System.out.println("DEBUG: ZIP/JAR resource:`"+filePath+"' found");
459
boolean found =
460             searchZipArchiveForFile( zf, fileName );
461             if (found) {
462             String JavaDoc host = addToFilePathCache( zf );
463             try {
464                 url = new URL( "extractor", host, 0, fileName );
465             }
466             catch (MalformedURLException mue) { }
467             }
468         }
469         }
470         else if ( file.exists() ) {
471         if (debug) System.out.println("DEBUG: resource file:`"+fileName+"' exists!");
472         String JavaDoc host = addToFilePathCache( new File(filePath) );
473         try {
474             url = new URL( "extractor", host, 0, fileName );
475         }
476         catch (MalformedURLException mue) { }
477         }
478     }
479     
480     if (debug) System.out.println("*FINAL* url = "+url+"\n");
481     
482     return (url);
483     }
484
485     /**
486      * A static method to retrieve a cached URL resource from the
487      * <code>Extractor</code> class loader modified class path.
488      * The method takes a URL with the protocol type
489      * <B><code>extracto:r</code></B> and attempts to retrieve the
490      * cached URL resource if possible. If the URL can be opened then
491      * an input stream is returned.
492      *
493      * @see ExtractorURLConnection#connect
494      *
495      * @param url the extractor URL
496      * @return <code>InputStream</code> If the cached URL is found
497      * inside a jar or zip file then a <code>ZipInputStream</code>
498      * is returned, otherwise a <code>FileInputStream</code> is
499      * returned.
500      *
501      * @exception IOException is thrown if the protocol is not
502      * <code>Extractor</code> or if the protocol is null, or if an
503      * error occurs reading the input stream.
504      *
505      */

506     public static InputStream getCachedResourceAsInputStream( URL url )
507            throws IOException
508     {
509     if (debug) System.out.println( "Extractor.getCachedResourceAsInputStream("+url+")" );
510
511     InputStream is;
512     URLConnection con = url.openConnection();
513     String JavaDoc protocol = url.getProtocol();
514     if ( protocol == null)
515         throw new IOException( "null protocol supplied.");
516     if ( !protocol.equalsIgnoreCase("extractor") )
517                throw new IOException( "protocol unrecognized by self extractor.");
518            
519     String JavaDoc host = url.getHost();
520     if (host.startsWith("FILE")) {
521         String JavaDoc filePath = (String JavaDoc)filePathCache.get(host);
522         if (filePath == null)
523         throw new IOException("no such extractor file resource:"+url.toExternalForm() );
524         String JavaDoc properFile = filePath + File.separatorChar + url.getFile();
525         is = new FileInputStream( properFile );
526     }
527     else if (host.startsWith("ZIP")) {
528         String JavaDoc filePath = (String JavaDoc)filePathCache.get(host);
529         String JavaDoc fileName = url.getFile();
530         if (filePath == null)
531         throw new IOException("no such extractor zip file resource:"+url.toExternalForm() );
532         is = getInputStreamFromZipFile( new File(filePath), fileName );
533         if (debug) {
534         System.out.println("\tThread="+Thread.currentThread());
535         System.out.println("\tThreadName="+Thread.currentThread().getName());
536         System.out.println("\tActiveCount="+Thread.currentThread().activeCount());
537         System.out.println("\tPriority="+Thread.currentThread().getPriority());
538         System.out.println("\tThreadGroup="+Thread.currentThread().getThreadGroup());
539         System.out.println("\tis="+is);
540         System.out.println("\tis.available()="+is.available() );
541         }
542     }
543     else
544         throw new IOException("unknown extractor resource -- cannot retrieve:"+url.toExternalForm() );
545
546     return (is);
547     }
548     
549     /** Searches for the filename entry inside a ZIP file or Java Archive.
550      * @param appresFileName the application resource filename.
551      * @return boolean return true if the entry can be found
552      * otherwise return false.
553      */

554     private static boolean searchZipArchiveForFile( File file, String JavaDoc appresFileName )
555     {
556     ZipFile zipFile = null;
557     boolean found = false;
558     
559     if ( !file.exists() ) return (false); // ZIP FILE DOES NOT EXISTS!!!
560

561     try {
562         // zipFile = openCachedZipFile( file );
563
zipFile = new ZipFile( file );
564         Enumeration entries = zipFile.entries();
565         while ( entries.hasMoreElements() ) {
566         ZipEntry zipEntry = (ZipEntry)entries.nextElement();
567         // System.out.println( "entry:"+zipEntry.getName() );
568
if (zipEntry.getName().equals( appresFileName ) ) {
569             if (debug) System.out.println( "*SFOUND* zip/jar resource:`"+appresFileName+"' ("+file.getPath()+")" );
570             found = true;
571             break;
572         }
573         }
574     }
575     catch (IOException e) {
576         e.printStackTrace();
577         System.err.println("file:"+file.getPath()+" "+appresFileName );
578         System.err.println("DEATH 69"); System.exit(69);
579     }
580     finally {
581         if (zipFile != null) try { zipFile.close(); } catch (IOException e) { };
582     }
583
584     return (found);
585     }
586
587     /** Read the file resource entry from inside a ZIP file
588      * or a Java Archive if possible.
589      * @param appresFileName the application resource filename.
590      * @return the input stream representing the application resources
591      * or null of the data could not be opened.
592      */

593     private static InputStream getInputStreamFromZipFile( File file, String JavaDoc appresFileName )
594     {
595     ZipFile zipFile = null;
596     boolean found=false;
597     InputStream is = null;
598     
599     if ( !file.exists() ) return (null); // ZIP FILE DOES NOT EXISTS!!!
600

601     ZipInputStream zis = null;
602     try {
603         zis = new ZipInputStream( new FileInputStream( file.getPath() ));
604         ZipEntry zipEntry;
605         do {
606         zipEntry = zis.getNextEntry();
607         if (zipEntry != null ) {
608             if (zipEntry.getName().equals( appresFileName ) ) {
609             if (debug) System.out.println( "*RFOUND* zip/jar resource:`"+appresFileName+"' ("+file.getPath()+")" );
610             is = zis;
611             found = true; /*!!*/
612             break;
613             }
614             else
615             zis.closeEntry(); // Close the entry.
616
}
617         }
618         while (zipEntry != null);
619     }
620     catch (IOException e) {
621         e.printStackTrace();
622         System.err.println("file:"+file.getPath()+" "+appresFileName );
623         System.err.println("**** DEATH 69 ****"); System.exit(69);
624     }
625     finally {
626         // Only close the final zip input stream if it was found!
627
if (!found)
628         try { is.close(); } catch (IOException e) { };
629     }
630
631     return (is);
632     }
633
634
635     /** used to print debuggable output */
636     private static void printChar( int ch )
637     {
638     if ( ch >= 32 && ch < 128 )
639         System.out.print((char)ch);
640     else
641         System.out.print("<"+ch+">");
642     }
643     
644     /** searches the entire input stream for a special key
645      * Once the special key is recognized, the functions stops reading
646      * @param pis the input stream
647      * @param key the special key string to search for
648      *
649      * @return true if the key was found.
650      */

651     private boolean findArchiveKey( PushbackInputStream pis, String JavaDoc key )
652     throws IOException
653     {
654     boolean archiveKeyFound=false;
655     int ch=0;
656
657     while ( ch != -1 ) {
658         int count=0;
659         for (int k=0; k<key.length(); ++k) {
660         ch = pis.read();
661         // printChar(ch); // DEBUG:
662
if ( ch == key.charAt(k))
663             ++count;
664         else
665             break;
666         }
667         if (count == key.length() ) {
668         archiveKeyFound = true;
669         break;
670         }
671         
672     }
673
674     return (archiveKeyFound );
675     }
676
677     /** reads the delimiter bar character ('|') from the input stream
678      * @param pis the input stream
679      */

680     private void readDelimiter( PushbackInputStream pis )
681     throws IOException
682     {
683     if (debug) System.out.println("readDelimiter");
684
685     int ch = pis.read();
686     // printChar(ch); // DEBUG:
687
if ( ch != '|' )
688         throw new IOException( "delimiter expected." );
689     }
690     
691     
692     /** reads the archive type from the input stream
693      * @param pis the input stream
694      *
695      * @return the archive type string
696      */

697     private String JavaDoc readArchiveType( PushbackInputStream pis )
698     throws IOException
699     {
700     if (debug) System.out.println("readArchiveType");
701
702     StringBuffer JavaDoc type = new StringBuffer JavaDoc(8);
703     int ch=0;
704
705     int counter=0;
706     do {
707         ch = pis.read(); ++counter;
708            // printChar(ch); // DEBUG:
709
if ( ch != '|' )
710         type.append((char)ch);
711         else
712         pis.unread(ch);
713     }
714     while ( ch != '|' && counter < 32 );
715
716     return type.toString();
717     }
718     
719     /** Reads the application class name from the input stream
720      * @param pis the input stream
721      *
722      * @return the application class name string
723      */

724     private String JavaDoc readApplicationClass( PushbackInputStream pis )
725     throws IOException
726     {
727     if (debug) System.out.println("readApplicationClass");
728     
729     StringBuffer JavaDoc appClass = new StringBuffer JavaDoc(8);
730     int ch=0;
731
732     int counter=0;
733     do {
734         ch = pis.read(); ++counter;
735         // printChar(ch); // DEBUG:
736
if ( ch != '|' )
737         appClass.append((char)ch);
738         else
739         pis.unread(ch);
740     }
741     while ( ch != '|' && counter < 512 );
742
743     return appClass.toString();
744     }
745
746     /** Reads the extra class path string
747      * @param pis the input stream
748      *
749      * @return the extra class path string
750      */

751     private String JavaDoc readExtraClassPath( PushbackInputStream pis )
752     throws IOException
753     {
754     if (debug) System.out.println("readExtraClassPath");
755     
756     StringBuffer JavaDoc ecp = new StringBuffer JavaDoc(64);
757     int ch=0;
758
759     int counter=0;
760     do {
761         ch = pis.read(); ++counter;
762         // printChar(ch); // DEBUG:
763
if ( ch != '|' )
764         ecp.append((char)ch);
765         else
766         pis.unread(ch);
767     }
768     while ( ch != '|' && counter < 512 );
769
770     return ecp.toString();
771     }
772
773     /** Extracts a zip or jar archive from the input stream
774      * @param pis the input stream
775      *
776      */

777     private void extractZipFiles( InputStream is )
778     throws IOException
779     {
780     if (debug) System.out.println( "extractZipFiles()" ); // DEBUG:
781

782     ZipInputStream zis = null;
783     try {
784         zis = new ZipInputStream( is );
785         ZipEntry zipEntry;
786         int num_entries=0;
787     
788         byte [] buffer = new byte[4096];
789         int total_bytes=0;
790         do {
791         zipEntry = zis.getNextEntry();
792         if (zipEntry != null ) {
793             ++num_entries;
794             int method = zipEntry.getMethod();
795             if (debug) {
796             System.out.println( "*** ENTRY ["+num_entries+"] ***" );
797             System.out.println( " name: " + zipEntry.getName() );
798             System.out.println( " size: " + zipEntry.getSize() );
799             System.out.println( " extra: " + zipEntry.getExtra() );
800             System.out.println( " compressed size: " + zipEntry.getCompressedSize() );
801             System.out.println( " method: " +
802                         (method == ZipEntry.DEFLATED ? "(Compressed)" :
803                          method == ZipEntry.STORED ? "(Stored)" : "UNKNOWN!" ));
804             }
805             System.out.print('.');
806
807             String JavaDoc entryFilePath = zipEntry.getName();
808             entryFilePath = entryFilePath.replace('/', File.separatorChar );
809             entryFilePath = entryFilePath.replace('\\', File.separatorChar );
810             if (debug) System.out.println( "extracting: `"+entryFilePath +"'");
811         
812         
813             File entryFile = new File( entryFilePath );
814             if (zipEntry.isDirectory() ) {
815             // This entry is a directory, try to create it.
816
entryFile.mkdirs();
817             }
818             else {
819             // Extract the zip file entry
820
if ( entryFile.getParent() != null )
821                 new File(entryFile.getParent()).mkdirs();
822             
823             extractedFiles.addElement( entryFilePath );
824             FileOutputStream fos = new FileOutputStream( entryFile );
825             int num_bytes;
826             while ( (num_bytes = zis.read( buffer, 0, buffer.length )) >= 0 ) {
827                 fos.write( buffer, 0, num_bytes );
828                 fos.flush(); // FLUSH IT!
829
total_bytes += num_bytes;
830             }
831             fos.close(); // CLOSE IT!!
832
}
833             zis.closeEntry();
834         }
835         }
836         while (zipEntry != null);
837     
838         System.out.println("\n\nExtracted a total "+total_bytes+" byte"+
839                    (total_bytes == 1 ? "" : "s" ) +
840                    " from "+ num_entries+" ent"+
841                    (num_entries == 1 ? "ry" : "ries" )+"." );
842     }
843     finally {
844         // Use the finally clause to make sure the stream is closed.
845
if (zis != null) try { zis.close(); } catch (IOException e ) { ; }
846     }
847     }
848
849     /** function to self extract the files from the extractor class
850      * @param pis the (push back) input stream
851      *
852      */

853     public void extractFiles()
854     {
855     String JavaDoc resname = getClass().getName();
856
857     String JavaDoc left ="-=+";
858     String JavaDoc right="+=-";
859     String JavaDoc key=left+"ARCHIVE"+right;
860     
861     PushbackInputStream pis=null;
862     try {
863         pis = new PushbackInputStream(
864         new FileInputStream( resname+".class" ));
865         int ch=0;
866         boolean foundKey=false;
867         //
868
// Search for the key in the stream if available.
869
//
870
foundKey = findArchiveKey( pis, key );
871         if ( foundKey ) {
872         if (debug) System.out.println("\n*** KEY FOUND !!! ***");
873         readDelimiter(pis);
874         archiveType = readArchiveType(pis);
875         readDelimiter(pis);
876         appClassName = readApplicationClass(pis);
877         readDelimiter(pis);
878         extraClassPath = readExtraClassPath(pis);
879         readDelimiter(pis);
880         
881         // Handle the case of extra new line character usually
882
// caused with the shell /bin/echo.
883
ch = pis.read();
884         if ( ch != '\n' ) {
885             pis.unread(ch);
886         }
887         
888         if (debug) {
889             System.out.println( "\n***DEBUG***" );
890             System.out.println( "archiveType=`"+archiveType+"'");
891             System.out.println( "appClassName=`"+appClassName+"'" );
892             System.out.println( "extraClassPath=`"+extraClassPath+"'");
893         }
894         
895         if ( archiveType.equalsIgnoreCase("ZIP" ) )
896             extractZipFiles( pis );
897         else
898             throw new RuntimeException JavaDoc("Cannot extract archive type:`"+archiveType+"'");
899         }
900         else
901         throw new RuntimeException JavaDoc("Cannot find archive key.");
902
903     }
904     catch (IOException ioe) {
905         System.err.println(ioe);
906         if (debug) ioe.printStackTrace();
907         cleanupFiles();
908         System.exit(1);
909     }
910     finally {
911         if (pis != null) try { pis.close(); } catch (IOException e) { ; }
912     }
913     
914     }
915     
916     /** Beginning self extracting process and runs the
917      * installer application class installer.
918      *
919      * Assuming self extraction of the application class worked, the
920      * application class is loaded dynamically and this class must
921      * have an method `public static void main ( String [] )' to work
922      * sucessfully. This main method of the application is then called
923      * and we are away. This method may not return.
924      *
925      * @param args an array of arguments to pass to the application
926      * class.
927      */

928     public void runApplicationInstaller( String JavaDoc [] args )
929     {
930     //
931
// Read the Extractor/launcher properties
932
//
933
readDefaultPropertyFile( new File("Extractor.properties"), false );
934
935     String JavaDoc tmp = System.getProperty("ext.debug");
936     if (tmp.equalsIgnoreCase("yes") || tmp.equalsIgnoreCase("on"))
937         debug=true;
938     else if (tmp.equalsIgnoreCase("no") || tmp.equalsIgnoreCase("off"))
939         debug=false;
940     
941     if (debug) {
942         System.out.println("**** DEBUG ****");
943         System.out.println(getClass().getName()+".runApplicationInstaller()" );
944     }
945
946     boolean launchProg=true;
947     tmp = System.getProperty("ext.launchprog");
948     if (tmp.equalsIgnoreCase("no") || tmp.equalsIgnoreCase("off"))
949         launchProg=false;
950
951     if (!launchProg) {
952         System.out.println("directed not to launch main application");
953         return;
954     }
955     
956     extractFiles();
957     
958     // // *** WARNING ***: This next line does not work!
959
// invokeClassLoader( args );
960

961     // THE FALLBACK
962
invokeJVM( args );
963     }
964     
965     /** read the default property file and merge it into
966      * system properties.
967      * @param propFile the default property file
968      * @param override true, if file properties override the default
969      * system properties if it exists
970      */

971     public boolean readDefaultPropertyFile( File propFile, boolean override )
972     {
973     if ( !propFile.exists() )
974         return (false);
975     
976     Properties propJVM = System.getProperties ();
977
978     //
979
// load the default properties files.
980
//
981
if (debug)
982         System.out.println("Loading default properties file: `"+propFile.getPath()+"'" );
983     if ( !propFile.canRead() ) {
984         System.err.println("Cannot read default properties file:`"+propFile.getPath()+"'" );
985         return (false);
986     }
987     
988     //
989
// Merge the default properties into the system properties
990
//
991
boolean retval=false;
992     Properties propTemp = new Properties();
993     try {
994         FileInputStream fis = new FileInputStream( propFile );
995         propTemp.load(fis);
996         Enumeration keys = propTemp.propertyNames();
997         while( keys.hasMoreElements() ) {
998         String JavaDoc name = (String JavaDoc)keys.nextElement();
999         String JavaDoc value1 = propTemp.getProperty( name );
1000        String JavaDoc value2 = propJVM.getProperty( name );
1001        if (value2 == null || override) {
1002            // DEBUG: System.out.println( "Merging ==> "+name+"="+value1 );
1003
propJVM.put( name, value1 );
1004        }
1005        }
1006    }
1007    catch (IOException ioe) {
1008        retval = false;
1009        System.err.println(
1010        "I/O Exception occurred reading file:" +
1011        propFile.getPath() + " message:`" + ioe.getMessage()+"'" );
1012    }
1013
1014    return (retval);
1015    }
1016
1017    private void invokeJVM( String JavaDoc [] args )
1018    {
1019    String JavaDoc osname = System.getProperty("os.name");
1020    String JavaDoc pathSep = System.getProperty("path.separator");
1021    String JavaDoc fileSep = System.getProperty("file.separator");
1022
1023
1024    if ( extraClassPath.length() > 0 ) {
1025        //
1026
// Modify the class path take into account
1027
// the difference between Window NT and UNIX systems
1028
//
1029

1030        if ( pathSep.equals(";") )
1031        // Win NT replace (:) with (;)
1032
extraClassPath = extraClassPath.replace( ':' , ';' );
1033        else
1034        // UNIX replace (;) with (:)
1035
extraClassPath = extraClassPath.replace( ';' , ':' );
1036        
1037        if ( fileSep.equals("\\") )
1038        // Win NT replace (/) with (\)
1039
extraClassPath = extraClassPath.replace( '/' , '\\' );
1040        else
1041        // UNIX replace (\) with (/)
1042
extraClassPath = extraClassPath.replace( '\\' , '/' );
1043
1044        // Properties propsJVM = System.getProperties();
1045
// String ncp = extraClassPath + pathSep +
1046
// System.getProperty("java.class.path");
1047
// propsJVM.put("java.class.path",ncp);
1048

1049        // if (debug)
1050
// System.out.println("*NEW* classpath = "+ System.getProperty("java.class.path") );
1051
}
1052    
1053    // FIXME: Wanted more sophisticated Java VM program search
1054
// In truth we'd like a `java.program.name' System property
1055
// and probably a `java.program.path' too
1056
String JavaDoc javaExe, jreExe, jrewExe;
1057    if ( osname.startsWith("Windows 95") ||
1058         osname.startsWith("Windows 98") ||
1059         osname.startsWith("Windows NT") ||
1060         osname.startsWith("Windows 2000") ||
1061         osname.startsWith("OS/2") ) {
1062        javaExe="java.exe";
1063        jreExe="jre.exe";
1064        jrewExe="jrew.exe";
1065    }
1066    else {
1067        javaExe="java";
1068        jreExe="jre";
1069        jrewExe="jrew";
1070    }
1071
1072    if (debug) {
1073        System.out.println("\n\tjavaExe="+javaExe+"\t *DEBUG*");
1074        System.out.println("\tjreExe="+jreExe);
1075        System.out.println("\tjrewExe="+jrewExe);
1076    }
1077    
1078    String JavaDoc jvmLauncher = System.getProperty("ext.jvm.launcher","");
1079    String JavaDoc tmp;
1080    if ( jvmLauncher.length() < 1 ) {
1081        tmp = System.getProperty("java.home")+fileSep+"bin"+ fileSep+javaExe;
1082        if ( new File(tmp).exists() )
1083        jvmLauncher = tmp;
1084        else {
1085        tmp = System.getProperty("java.home")+fileSep+"bin"+ fileSep+jreExe;
1086        if ( new File(tmp).exists() )
1087            jvmLauncher = tmp;
1088        else {
1089            tmp = System.getProperty("java.home")+fileSep+"bin"+ fileSep+jrewExe;
1090            if ( new File(tmp).exists() )
1091            jvmLauncher = tmp;
1092        }
1093        }
1094    }
1095    
1096    if ( jvmLauncher.length() < 1 ) {
1097        System.err.println("BUMMER: What Java Virtual Machine interpreter should I use?");
1098        System.err.println("Set the system property `ext.jvm.launcher' to path of your JVM.");
1099        System.exit(69);
1100    }
1101
1102    //
1103
// Get options for virtual machine interpreter
1104
//
1105
String JavaDoc options="";
1106
1107    // Get JIT
1108
tmp = System.getProperty("ext.jvm.option.jit","");
1109    if (tmp.equalsIgnoreCase("yes") || tmp.equalsIgnoreCase("on"))
1110        options +="-jit ";
1111    else if (tmp.equalsIgnoreCase("no") || tmp.equalsIgnoreCase("off") )
1112        options +="-nojit ";
1113    
1114    // Java compiler JIT settings
1115
tmp = System.getProperty("ext.jvm.option.java.compiler");
1116    if (tmp != null && tmp.length() > 1 )
1117        options +="-Djava.compiler="+tmp.trim()+" ";
1118
1119    // Verbose setting
1120
tmp = System.getProperty("ext.jvm.option.verbose","");
1121    if (tmp.equalsIgnoreCase("yes") || tmp.equalsIgnoreCase("on"))
1122        options +="-verbose ";
1123
1124    // Verbose GC setting
1125
tmp = System.getProperty("ext.jvm.option.verbosegc","");
1126    if (tmp.equalsIgnoreCase("yes") || tmp.equalsIgnoreCase("on"))
1127        options +="-verbosegc ";
1128
1129    // Verify setting
1130
tmp = System.getProperty("ext.jvm.option.verify","");
1131    if (tmp.equalsIgnoreCase("remote") )
1132        options +="-verifyremote ";
1133    else if (tmp.equalsIgnoreCase("local") )
1134        options +="-verify ";
1135    else if (tmp.equalsIgnoreCase("none") )
1136        options +="-noverify ";
1137    
1138    // Verbose setting
1139
tmp = System.getProperty("ext.jvm.option.debug","");
1140    if (tmp.equalsIgnoreCase("yes") || tmp.equalsIgnoreCase("on"))
1141        options +="-debug";
1142
1143    // Native stack maximum size setting
1144
tmp = System.getProperty("ext.jvm.option.native.stack.maxsize");
1145    if (tmp != null && tmp.length() > 1 )
1146        options += "-ss"+tmp.trim()+" ";
1147    
1148    // Java stack maximum size setting
1149
tmp = System.getProperty("ext.jvm.option.java.stack.maxsize");
1150    if (tmp != null && tmp.length() > 1 )
1151        options += "-oss"+tmp.trim()+" ";
1152    
1153    // Java heap maximum size setting
1154
tmp = System.getProperty("ext.jvm.option.java.heap.maxsize");
1155    if (tmp != null && tmp.length() > 1 )
1156        options += "-mx"+tmp.trim()+" ";
1157    
1158    // Java heap initial size setting
1159
tmp = System.getProperty("ext.jvm.option.java.heap.initsize");
1160    if (tmp != null && tmp.length() > 1 )
1161        options += "-ms"+tmp.trim()+" ";
1162    
1163    // Java thread model setting
1164
tmp = System.getProperty("ext.jvm.option.thread.model","");
1165    if (tmp.equalsIgnoreCase("green") )
1166        options +="-green ";
1167    else if (tmp.equalsIgnoreCase("native") )
1168        options +="-native ";
1169
1170    // Extra command line options setting
1171
tmp = System.getProperty("ext.jvm.option.extraflags");
1172    if (tmp != null)
1173        options += tmp + " ";
1174
1175    // Extra system class path setting
1176
String JavaDoc extraSystemClassPath = System.getProperty("ext.jvm.extra.class.path");
1177
1178    // Trace standard output channel of the launch process
1179
boolean traceStdout=false;
1180    tmp = System.getProperty("ext.jvm.trace.stdout");
1181    if (tmp.equalsIgnoreCase("yes") || tmp.equalsIgnoreCase("on"))
1182        traceStdout=true;
1183
1184    // Trace standard error channel of the launch process
1185
boolean traceStderr=false;
1186    tmp = System.getProperty("ext.jvm.trace.stderr");
1187    if (tmp.equalsIgnoreCase("yes") || tmp.equalsIgnoreCase("on"))
1188        traceStderr=true;
1189
1190    try {
1191        Runtime JavaDoc runtime = Runtime.getRuntime();
1192        String JavaDoc newClassPath = extraClassPath+pathSep+System.getProperty("java.class.path");
1193        if ( extraSystemClassPath != null)
1194        newClassPath = extraSystemClassPath+pathSep+newClassPath;
1195        String JavaDoc finalCmd =
1196        jvmLauncher + " " +options+ " -classpath "+newClassPath+" "+
1197        appClassName;
1198    
1199        if (debug) System.out.println( "*DEBUG* finalCmd = `"+finalCmd+"'" );
1200        System.out.println();
1201        Process JavaDoc process = runtime.exec( finalCmd );
1202        Thread JavaDoc tid1=null, tid2=null;
1203        
1204        if (traceStdout) {
1205        InputStream is = process.getInputStream();
1206        // System.out.println( "(DEBUG:) is = "+is );
1207
// System.out.println( "is.available() = "+is.available() );
1208
tid1 = new Thread JavaDoc( new ExtractorProcessOutputReader( is, System.out ));
1209        tid1.start();
1210        }
1211        
1212        if (traceStderr) {
1213        InputStream isError = process.getInputStream();
1214        // System.out.println( "(DEBUG:) isError = "+isError );
1215
// System.out.println( "isError.available() = "+isError.available() );
1216
tid2 = new Thread JavaDoc( new ExtractorProcessOutputReader( isError, System.err ));
1217        tid2.start();
1218        }
1219        
1220        process.waitFor();
1221        if (debug) System.out.println("DEBUG: exitValue:"+process.exitValue());
1222
1223        // Wait for the process output reader thread to join
1224
if (tid1 != null) tid1.join();
1225        if (tid2 != null) tid2.join();
1226    }
1227    catch (IOException ioe ) {
1228        System.err.println( ioe );
1229    }
1230    catch (InterruptedException JavaDoc ie ) {
1231        System.err.println( ie );
1232    }
1233    }
1234    
1235
1236    /**
1237     * A method to automatically start an application by loading an object
1238     * using this class <code>Extractor</code>, which is also a
1239     * <code>ClassLoader</code>, then use Java reflection mechanism
1240     * to dynamically call the
1241     * <code>public static void <B>main</B>( String [] args) </code>
1242     *
1243     * <P>
1244     * <B>Unfortunately</B> it does not work at all across packages.
1245     * Although the class loader works with the freeinstaller itself
1246     * namely <I>freeinst.jar</I>.
1247     * <P>
1248     * Here is the log file:
1249     * <P>
1250     *
1251     * <PRE>
1252     * peterp@hobbit [319] > java Extractor
1253     * Extractor.runInstaller()
1254     *
1255     * *** KEY FOUND !!! ***
1256     * readDelimiter
1257     * readArchiveType
1258     * readDelimiter
1259     * readApplicationClass
1260     * readDelimiter
1261     * readExtraClassPath
1262     * readDelimiter
1263     *
1264     * ***DEBUG***
1265     * archiveType=`ZIP'
1266     * appClassName=`xenon.xsql.installer.XsqlInstaller'
1267     * extraClassPath=`.:freeinst.jar'
1268     * extractZipFiles()
1269     * *** ENTRY [1] ***
1270     * name: fish/TEA.txt
1271     * size: 33
1272     * extra: null
1273     * compressed size: 33
1274     * method: (Stored)
1275     * .extracting: `fish/TEA.txt'
1276     *
1277     *
1278     * Extracted a total 33 bytes from 1 entry.
1279     * DEBUG: Extractor.loadClass(xenon.xsql.installer.XsqlInstaller,true)
1280     * loaded class:`xenon.xsql.installer.XsqlInstaller'
1281     * created a new instance of it.
1282     * about to invoke `public static void main( String [] )'
1283     * Method threw an java.lang.NoClassDefFoundError: ixenon/free/install/FreeInstallerApplication
1284     * java.lang.reflect.InvocationTargetException
1285     * at Extractor.runApplicationInstaller(Extractor.java:1005)
1286     * at Extractor.main(Extractor.java:1089)
1287     * nested exception
1288     * java.lang.NoClassDefFoundError: ixenon/free/install/FreeInstallerApplication
1289     * at Extractor.runApplicationInstaller(Extractor.java:1005)
1290     * at Extractor.main(Extractor.java:1089)
1291     * Extractor.finalize()
1292     * ========================================
1293     * cleaning up files
1294     * deleting file:`fish/TEA.txt'
1295     * peterp@hobbit [320] >
1296     * </PRE>
1297     */

1298    private void invokeClassLoader( String JavaDoc [] args )
1299    {
1300
1301    String JavaDoc osname = System.getProperty("os.name");
1302    String JavaDoc pathSep = System.getProperty("path.separator");
1303    String JavaDoc fileSep = System.getProperty("file.separator");
1304    
1305    //
1306
//
1307
//
1308
// Now try to load the Extractor's invocation class,
1309
// which must have a `public static void main( String [] )' method.
1310
// Notice I am using reflection to do this.
1311
//
1312
try {
1313        String JavaDoc [] dummyArgs = new String JavaDoc [0];
1314        final Extractor extractorCL = this;
1315        final Class JavaDoc faiClass = extractorCL.loadClass( "ixenon.free.install.FreeInstallerApplication");
1316        if (debug) System.out.println( "loaded class:`"+faiClass.getName()+"'" );
1317
1318        final Class JavaDoc mainClass = extractorCL.loadClass( appClassName );
1319        if (debug) System.out.println( "loaded class:`"+appClassName+"'" );
1320      
1321        final Object JavaDoc mainObj = mainClass.newInstance();
1322        if (debug) System.out.println( "created a new instance of `"+mainClass.getName()+"'");
1323        
1324        final Method mainMethod = mainClass.getMethod(
1325        "main", new Class JavaDoc [] { dummyArgs.getClass() } );
1326        if (debug) System.out.println( "about to invoke `public static void main( String [] )'");
1327        System.out.println();
1328        Object JavaDoc retVal = mainMethod.invoke( mainObj, new Object JavaDoc [] { args } );
1329        // *PP* Of course the `retVal' is a java.lang.reflect.Void object.
1330
}
1331    catch ( ClassNotFoundException JavaDoc e1 ) {
1332        System.err.println(e1);
1333        e1.printStackTrace();
1334    }
1335    catch ( InstantiationException JavaDoc e2 ) {
1336        System.err.println(e2);
1337        e2.printStackTrace();
1338    }
1339    catch ( NoSuchMethodException JavaDoc e3 ) {
1340        System.err.println(e3);
1341        e3.printStackTrace();
1342    }
1343    catch ( IllegalAccessException JavaDoc e4 ) {
1344        System.err.println(e4);
1345        e4.printStackTrace();
1346    }
1347    catch ( InvocationTargetException e5 ) {
1348        System.err.println("Method threw an "+e5.getTargetException() );
1349        e5.printStackTrace();
1350        System.err.println ("Nested exception");
1351        e5.getTargetException().printStackTrace();
1352    }
1353    }
1354
1355    /** A method to clean up extracted automatically */
1356    public void cleanupFiles()
1357    {
1358    boolean doClean=true;
1359    String JavaDoc tmp = System.getProperty("ext.cleanup");
1360    if (tmp.equalsIgnoreCase("no") || tmp.equalsIgnoreCase("off"))
1361        doClean=false;
1362
1363    if (!doClean) return;
1364
1365    System.out.println("========================================");
1366    System.out.println("Cleaning up extracted files");
1367    
1368    Enumeration etor = extractedFiles.elements();
1369    int k=0;
1370    while ( etor.hasMoreElements() ) {
1371        String JavaDoc filename = (String JavaDoc)etor.nextElement();
1372        File file = new File( filename );
1373        if ( file.exists() ) {
1374        if (debug) System.out.println(" ["+k+"] deleting file:`"+filename+"'" );
1375        file.delete();
1376        ++k; /* ! */
1377        }
1378    }
1379    extractedFiles.removeAllElements();
1380    }
1381
1382    /** the defensive and safe to make sure extracted files are
1383     * cleaned up at exit.
1384     *
1385     * @exception alway define finalize throw <B>THROWABLE</B>
1386     */

1387    public void finalize()
1388    throws Throwable JavaDoc
1389    {
1390    try {
1391        if (debug) System.out.println("Extractor.finalize()");
1392        cleanupFiles();
1393    }
1394    finally {
1395        super.finalize(); // ALWAYS chain finalize methods
1396
}
1397    }
1398    
1399    public static void main( String JavaDoc [] args )
1400    {
1401    // We are using deprecated 1.2 method is anycase to get the extractor
1402
// to clean up files and exits.
1403
//
1404
// ----------------------------------------------------------------------
1405
// System.runFinalizersOnExit( boolean flag )
1406
// DEPRACATED. This method is inherently unsafe. It may result in
1407
// finalizers being called on live objects, resulting in erratic
1408
// behaviour or deadlock
1409
// ----------------------------------------------------------------------
1410
//
1411
// I don't know why Sun would introduce a new API feature 1.1 and then
1412
// immediately depracate it in Java 1.2 Beta 4. Clearly it says that
1413
// that the JVM currently available are unsafe, because the Java
1414
// language is incomplete requires a new keyword to distinguish
1415
// between finalizing objects and those that are not, and a concept
1416
// to schedule the finalization within threads.
1417
// *PP* Peter Pilgrim Mon Feb 22 22:35:37 GMT 1999
1418
System.runFinalizersOnExit(true);
1419
1420    System.out.println("Java Self Extracting Class - XeNoNSoFT [c] 1999 ");
1421    System.out.println("This extractor program is a part of the `FreeInstaller' ");
1422    System.out.println("distribution, which is \"available as free software\".");
1423    System.out.println("For more details, please read the `LICENSE-XPeL.txt'\n");
1424    extractor = new Extractor();
1425    extractor.runApplicationInstaller( args );
1426    }
1427    
1428
1429    /**
1430     * Creates a new <code>URLStreamHandler</code> instance with the specified
1431     * protocol.
1432     *
1433     * Implements the factory method to create a URL.
1434     * This method is required to add the <B>extractor</B> protocol.
1435     *
1436     * @param protocol the protocol ("<code>ftp</code>",
1437     * "<code>http</code>", "<code>nntp</code>", etc.).
1438     * @return a <code>URLStreamHandler</code> for the specific protocol.
1439     * @see java.io.URLStreamHandler
1440     *
1441     */

1442    public URLStreamHandler createURLStreamHandler( String JavaDoc protocol )
1443    {
1444    if (debug) System.out.println( "Extractor.createURLStreamHander("+protocol+")" );
1445    if (protocol.equalsIgnoreCase("extractor")) {
1446        return new ExtractorURLStreamHandler();
1447    }
1448    else {
1449        return (null);
1450    }
1451    }
1452    
1453        
1454}
1455
1456// fini
1457
Popular Tags