KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > yagga > util > MetaJarResources


1 /*
2  * This file is part of MiniInstaller, a self installer builder for Java
3  * Copyright (C) 2002 Walter Gamba
4  * mailto:walter@yagga.net
5  * http://www.yagga.net/java/miniinstaller
6  *
7  * MiniInstaller is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * MiniInstaller is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  *
21  * As the time of writing, the GNU General Public Licene can be
22  * found at http://www.gnu.org/licenses/gpl.txt
23  *
24  */

25
26 package net.yagga.util;
27
28 import java.io.*;
29 import java.util.*;
30 import java.util.jar.*;
31 import java.net.*;
32
33 /**
34  * Class that implements reading data from a Jar file. The Jar file can be in the class
35  * path or included in the jar file we are executing!.
36  *
37  * This class reads bytes arrays for a given entry in a given jar file. If the case is the simple
38  * one:
39  * <pre>
40  *
41  * ./Test.class
42  * ./net/yagga/util/MetaJarResource.class
43  * ./sample.jar
44  * +- icon.gif
45  * </pre>
46  *
47  * one can read data from the file "sample.jar" via this class, in the main method of the Test class.
48  * This case is more or less the same as seen in JavaWorld's tip 49 (JarResource). The class reads the
49  * specified Jar entry and returns the byte array. The trick is a bit more complicated if we are in such a case:
50  * <pre>
51  *
52  * ./all.jar
53  * +- Test.class
54  * +- net/yagga/util/MetaJarResource.class
55  * +- sample.jar
56  * +- icon.gif
57  * </pre>
58  * If we execute the Jar file "all.jar" Test class cannot find a resource inside the sample.jar file using the
59  * usual trick in JArResource. We must extract in a JarResource way from the top level Jar (all.jar) the entry for
60  * sample.jar, then extracting from it the entry from icon.gif.
61  * I have not found a simple way to do this:<BR>
62  * <OL>
63  * <LI>I extract the bytes for the sample.jar entry of the top level jar</LI>
64  * <LI>I save those bytes in the /tmp dir of the System</LI>
65  * <LI>I read from this actual file the sizes of each entry in the sub-jar file (sample.jar)</LI>
66  * <LI>I delete the temp file, and then I read from the bytes of the sub jar the given entry, for the number of bytes I have obtained in step 3</LI>
67  * </OL>
68  * @see <a HREF="http://www.javaworld.com/javaworld/javatips/jw-javatip49.html"> JavaWorld's tip 49</a>
69  *
70  * @author Walter Gamba
71  */

72 public class MetaJarResources
73 {
74     private Hashtable htSizes=new Hashtable();
75     private String JavaDoc jarFile;
76
77     //for self referencing jar
78
private boolean executingFromJar=false; //true if this class is executed inside a jar
79
private Hashtable metaJarContent=new Hashtable();
80     private String JavaDoc jarJarFile;
81     private Manifest metaManifest=null;
82
83     /**
84      * Construct a MetaJarResources file, a jar file that can be in the class path (simple case) or
85      * enclosed in the same jar file that is executing now.
86      *
87      * This constructor decides if the environment in which this class is called is
88      * <OL>
89      * <LI>we are created from a class in the classpath</LI>
90      * <LI>we are created from a class included in a Jar in the classpath</LI>
91      * </OL>
92      * and consequently if the jar file passed is in the classspath or enclosed in the jar file we are
93      * currently executing.<BR>
94      * Note that the behaviour of the class in either case is transparent regards to the user.
95      *
96      * @param jFile the name of the Jar file we want to read resources from.
97      */

98     public MetaJarResources(String JavaDoc jFile)
99     {
100         super();
101         this.jarFile=jFile;
102
103         try{
104             URL u=getClass().getResource("/"+jarFile);
105             //Ut.infoln("JAR URL of "+jarFile+" is"+u);
106
URLConnection uc=null;
107             if(u!=null)
108                 uc=u.openConnection();
109
110             if(uc instanceof JarURLConnection)
111             {
112                 executingFromJar=true;
113                 JarURLConnection juc = (JarURLConnection)uc;
114                 jarJarFile=jarFile;
115                 //jar file is containing file, not the one specified..
116
//trying to get this name..
117
jarFile=juc.getJarFileURL().getFile();
118                         //now take care of 1.3/1.4 glitches:
119
//in JDK1.3 the URL of getResource did not translate spaces to %20
120
//in jdk1.4 it does (correctly) .. so in order
121
//to account for file names with spaces..
122
jarFile=URLDecoder.decode(jarFile);
123                 //read in sizes for top level jar, anyway
124

125                 initTopJarSizes();
126                 //reads jar-in-jar file
127
readJarInJar();
128             }
129             else //if (uc instanceof FileURLConnection){
130
{
131                 executingFromJar=false;
132                 initTopJarSizes();
133             }
134         }
135         catch(IOException ioe){
136             Ut.error("Error getting file from file"+jarFile+":"+ioe);
137             jarFile="";
138         }
139     }
140
141     /**
142      * Method that actually reads raw bytes.
143      *
144      * this method reads in the appropriate way raw bytes for the given entry.
145      * @param entryName a String containing the full qualified name of the entryi in the jar file
146      * the name must be given in the usual directory/filename format, for example<BR>
147      * "img/icon.gif"<br>
148      * "net/yagga/test/Test.class"<BR>
149      *
150      * @return the array of bytes read foir the given entry, or null if sonething went wrong
151      */

152     public byte[] getBytes(String JavaDoc entryName)
153     {
154         byte b1[]=null;
155         //Ut.infoln("MJR:getBytes execFormJar="+executingFromJar+", entry="+entryName);
156
if(executingFromJar)
157             b1=(byte [])metaJarContent.get(entryName);
158         else
159             b1=readBytesFromTopJar(entryName);
160         return b1;
161     }
162
163     /**
164      * Return the manifest for the jar file.
165      * This can be the manifest of the plain Jar or the manifest of the jar file enclosed in the executing jar file.
166      * @return the Manifest of the jar file, or null if there is an error.
167      */

168     public Manifest getManifest()
169     {
170         try{
171             if(executingFromJar)
172                 return metaManifest;
173             else
174             {
175                 JarFile jf=new JarFile(jarFile);
176                 return jf.getManifest();
177             }
178         }catch(IOException ioe){
179             Ut.error("IOE getting main class "+ioe);
180             return null;
181         }
182     }
183
184     /**
185      * Return the actual jar name.
186      * This can be the top level Jar (whose name we usually ignore) or the jar file name
187      * we have passed in the constructor.
188      * @return the file name of the top-level jar file (if searching a Jar from inside an executing jar) or
189      * the jar name as we have passed it to the Constructor.
190      */

191     public String JavaDoc getActualJarName(){
192         if(executingFromJar)
193           return jarJarFile;
194         else
195           return jarFile;
196     }
197
198     /**
199      * Reads in self contained jar file.
200      *
201      * This is the case if the jar file we want to read is jarred in the jar currently executing
202      * so:
203      * <PRE>
204      * FOO.jar
205      * +- Installer.class
206      * +- net/yagga/util/MetaJarResources.class
207      * +- PACK1.jar
208      * +- ClassIWantToInvoke.class <===
209      * </PRE>
210      */

211     private void readJarInJar()
212     {
213         try
214         {
215             //read entry from top-level jar.
216
//the bytes read are bytes of a JAR file contained in this top-level jar file
217
byte[] jarE=readBytesFromTopJar(jarJarFile);
218
219       //if jarJarFile has a path preceding its name strip it...
220
int idx=jarJarFile.lastIndexOf("/"); //UNIX style
221
if(idx==-1)
222         idx=jarJarFile.lastIndexOf("\\"); //DOS STYLE
223
if(idx>=0)
224         jarJarFile=jarJarFile.substring(idx+1);
225
226             //save jar contained in this jar in a temp file
227
//so save PACK1.jar in a temp file
228

229             //to get temp dir
230
String JavaDoc tmpDir=System.getProperty("java.io.tmpdir");
231
232             File jarInJarFile=new File(tmpDir,jarJarFile);
233             FileOutputStream fout=new FileOutputStream(jarInJarFile.getCanonicalPath());
234
235             //save bytes in temp jar file
236
for(int i=0;i<jarE.length;i++)
237                 fout.write(jarE[i]);
238             fout.close();
239             fout=null;
240
241             //reads in temp jar file sizes (in this case, PACK1.jar)
242
Hashtable sizes=new Hashtable();
243             JarFile jf=new JarFile(jarInJarFile);
244             try
245             {
246                 Enumeration e=jf.entries();
247                 while (e.hasMoreElements())
248                 {
249                     JarEntry ze=(JarEntry)e.nextElement();
250                     //System.out.println("Entry:"+ze+", Size="+ze.getSize()+", CSize="+ze.getCompressedSize());
251
sizes.put(ze.getName(),new Integer JavaDoc((int)ze.getSize()));
252                 }
253                 //reads in Manifesdt
254
metaManifest=jf.getManifest();
255                 jf.close();
256             }
257             catch(IOException ioe)
258             {
259                 Ut.error("Error reading sizes in '"+fout+"':"+ioe);
260             }
261
262             //once I've read dims i can read data
263
//and thus I can safely delete temp jar file
264
jarInJarFile.delete();
265
266             //read in data from byte array
267
ByteArrayInputStream bais=new ByteArrayInputStream(jarE);
268             JarInputStream jis=new JarInputStream(bais);
269             JarEntry je=null;
270             while ((je=jis.getNextJarEntry())!=null)
271             {
272                 if (je.isDirectory())
273                     continue;
274
275                 int size=(int)je.getSize();
276                 if(size==-1)
277                     size=((Integer JavaDoc)sizes.get(je.getName())).intValue();
278
279                 byte[] b=new byte[(int)size];
280                 int rb=0;
281                 int chunk=0;
282                 while (((int)size - rb) > 0)
283                 {
284                     chunk=jis.read(b,rb,(int)size - rb);
285                     if (chunk==-1)
286                         break;
287                     rb+=chunk;
288                 }
289                 //save data in hash map
290
metaJarContent.put(je.getName(),b);
291
292                 //System.out.println("Meta entry+ URL="+je+", sz="+size);
293
}
294             jis.close();
295         }
296         catch(FileNotFoundException fnfe)
297         {
298             Ut.error(fnfe.toString());
299         }
300         catch(IOException ioe){
301             Ut.error(ioe.toString());
302         }
303     }
304
305     /**
306      * Read the sizes for top-level JAR file.
307      * This can be the sole JAR file, or
308      * it can be the top level jar file from which we are executing thic class, and in this
309      * case sizes can be used to read further into the JAR, for another JAR file
310      */

311     private void initTopJarSizes()
312     {
313         try
314         {
315             JarFile zf=new JarFile(jarFile);
316             Enumeration e=zf.entries();
317             while (e.hasMoreElements())
318             {
319                 JarEntry ze=(JarEntry)e.nextElement();
320                 htSizes.put(ze.getName(),new Integer JavaDoc((int)ze.getSize()));
321             }
322             zf.close();
323         }
324         catch(IOException ioe){
325             System.err.println("TOP Error reading sizes in '"+jarFile+"':"+ioe);
326         }
327     }
328
329     /**
330      * Reads bytes from Top level Jar file.
331      * Can be used standalone, or to read in another Jar file
332      * and then, using readJarInJar, filling the metaJarContent hashtable
333      * @param entryName a String containig the top-level entry we eant to read. Ther entry mustbe given in the
334      * usual directory/filename format
335      * @return an array poff bytes for the entry, or null if there was an erro
336      */

337     private byte[] readBytesFromTopJar(String JavaDoc entryName)
338     {
339         try
340         {
341             JarInputStream zis=getTopJIS();
342             JarEntry ze=null;
343             while ((ze=zis.getNextJarEntry())!=null)
344             {
345                 if (ze.isDirectory())
346                     continue;
347
348                 int size=(int)ze.getSize();
349                 if (size==-1)
350                     size=((Integer JavaDoc)htSizes.get(ze.getName())).intValue();
351
352                 if(ze.getName().equals(entryName))
353                 {
354                     //Ut.infoln(">>>>Entry:"+ze.getName()+", Size="+ze.getSize()+", CSize="+ze.getCompressedSize());
355
byte[] b=new byte[(int)size];
356                     int rb=0;
357                     int chunk=0;
358                     while (((int)size - rb) > 0)
359                     {
360                         chunk=zis.read(b,rb,(int)size - rb);
361                         if (chunk==-1)
362                         break;
363                         rb+=chunk;
364                     }
365                     zis.close();
366                     return b;
367                 }
368             }
369             zis.close() ;
370         }
371         catch (NullPointerException JavaDoc e) {
372             Ut.error("'"+jarFile+"':(done)"+e);
373         } catch (FileNotFoundException e) {
374             Ut.error("'"+jarFile+"':"+e);
375         } catch (IOException e) {
376             Ut.error("'"+jarFile+"':"+e);
377         }
378         return null;
379     }
380
381     /**
382      * Get top level JAR input stream.
383      * @return the JarInputStream associated with the top level jar file
384      */

385     private JarInputStream getTopJIS()
386     {
387         //
388
try{
389             JarFile jf=new JarFile(jarFile);
390             if(jf!=null)
391                 return new JarInputStream(new FileInputStream(jarFile));
392         }
393         catch(Exception JavaDoc e)
394         {
395             Ut.error("ERROR:"+e);
396         }
397
398         try
399         {
400             //System.out.println("getTopJIS "+jarFile);
401
InputStream is=openResource(jarFile);
402             return new JarInputStream(is);
403         }
404         catch(IOException io){
405             Ut.error("IOE:"+io);
406         }
407         return null;
408     }
409
410     /**
411      * open a stream from the environment.
412      * It can be from the file system or from inside the top-level jar
413      * @param filename the filename of the resource we want to read
414      * @return the InputStram associated with the given resource
415      */

416     private InputStream openResource(String JavaDoc filename)
417     {
418         try
419         {
420             //System.err.println("MJR:openResource:"+filename);
421
InputStream is=getClass().getResourceAsStream("/"+filename);
422             is.available();//to generate null ptr exceptio
423
return is;
424         }
425         catch (java.io.IOException JavaDoc e)
426         {
427             Ut.error("RM2: file not found:'"+filename+"'");
428             System.exit(1);
429         }
430         catch(NullPointerException JavaDoc e2){
431             Ut.error("RM3: file not found:'"+filename+"'");
432             //e2.printStackTrace();
433
System.exit(1);
434         }
435         return null;
436     }
437
438 }
439
440
Popular Tags