OSDN Git Service

ed7426512f6cdc674d557fdef90d7cc1587a82bf
[pf3gnuchains/gcc-fork.git] / libjava / java / net / JarURLConnection.java
1 /* JarURLConnection.java -- Class for manipulating remote jar files
2    Copyright (C) 1998 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10  
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package java.net;
40
41 import java.net.*;
42 import java.io.*;
43 import java.util.jar.*;
44 import java.util.zip.*;
45 import java.util.Map;
46 import java.util.Vector;
47 import java.util.Hashtable;
48 import java.security.cert.Certificate;
49
50 /**
51  * @author Kresten Krab Thorup <krab@gnu.org>
52  * @since 1.2
53  * @date Aug 10, 1999.
54  */
55
56
57 public abstract class JarURLConnection extends URLConnection
58 {
59   // three different ways to say the same thing
60   private final URL jarFileURL;
61
62   /** The connection to the jar file itself. A JarURLConnection
63    *  can represent an entry in a jar file or an entire jar file.  In
64    *  either case this describes just the jar file itself. */
65   protected URLConnection jarFileURLConnection;
66
67   // If this is a connection to a jar file element this is set, otherwise null.
68   private final String element;
69
70   // Cached JarURLConnection's 
71   static Hashtable conn_cache = new Hashtable();
72
73   public URL getJarFileURL ()
74   {
75     return jarFileURL;
76   }
77
78   public String getEntryName ()
79   {
80     return element;
81   }
82
83   /**
84    * Creates a new JarURLConnection
85    *
86    * @exception MalformedURLException If url is invalid
87    *
88    * @specnote This constructor is protected since JDK 1.4
89    */
90   protected JarURLConnection(URL url)
91     throws MalformedURLException
92   {
93     super(url);
94
95     String spec = url.getFile();
96     int bang = spec.indexOf ("!/", 0);
97     if (bang == -1)
98       throw new MalformedURLException (url + ": No `!/' in spec.");
99
100     // Extact the url for the jar itself.
101     jarFileURL = new URL(spec.substring (0, bang));
102
103     // Get the name of the element, if any.
104     element = (bang+2==spec.length() ? null : spec.substring (bang+2));
105   }
106
107   public synchronized void connect() throws IOException
108   {
109     // Call is ignored if already connected.
110     if (connected)
111       return;
112
113     if (getUseCaches())
114       {
115         jarFileURLConnection = (URLConnection) conn_cache.get (jarFileURL);
116
117         if (jarFileURLConnection == null)
118           {
119             jarFileURLConnection = jarFileURL.openConnection ();
120             jarFileURLConnection.setUseCaches (true);
121             jarFileURLConnection.connect ();
122             conn_cache.put (jarFileURL, jarFileURLConnection);
123           }
124       }
125     else
126       {
127         jarFileURLConnection = jarFileURL.openConnection ();
128         jarFileURLConnection.connect ();
129       }
130
131     connected = true;
132   }
133
134   public InputStream getInputStream() throws IOException
135   {
136     if (!connected)
137       connect();
138
139     if (! doInput)
140       throw new ProtocolException("Can't open InputStream if doInput is false");
141
142     if (element == null)
143       {
144         // This is a JarURLConnection for the entire jar file.  
145
146         InputStream jar_is = new BufferedInputStream(
147                         jarFileURLConnection.getInputStream ());
148         return new JarInputStream(jar_is);
149       }
150
151     // Reaching this point, we're looking for an element of a jar file.
152
153     JarFile jarfile = null;
154
155     try
156       {
157         jarfile = getJarFile ();
158       }
159     catch (java.io.IOException x)
160       {
161         /* ignore */
162       }
163     
164     if (jarfile != null)
165       {
166         // this is the easy way...
167         ZipEntry entry = jarfile.getEntry(element);
168         if (entry != null)
169           return jarfile.getInputStream (entry);
170         else
171           return null;
172       }
173     else
174       {
175         // If the jar file is not local, ...
176         JarInputStream zis = new JarInputStream(
177                         jarFileURLConnection.getInputStream ());
178
179         // This is hideous, we're doing a linear search...
180         for (ZipEntry ent = zis.getNextEntry (); 
181              ent != null; 
182              ent = zis.getNextEntry ())
183           {
184             if (element.equals (ent.getName ()))
185               {
186                 int size = (int)ent.getSize();
187                 byte[] data = new byte[size];
188                 zis.read (data, 0, size);
189                 return new ByteArrayInputStream (data);
190               }
191           }
192       }
193
194     return null;
195   }
196
197   /**
198    * Return the JAR entry object for this connection, if any
199    *
200    * @exception IOException If an error occurs
201    */
202   public JarEntry getJarEntry () throws IOException
203   {
204     JarFile jarfile = null;
205
206     if (element == null)
207       return null;
208
209     if (! doInput)
210       throw new ProtocolException("Can't open JarEntry if doInput is false");
211
212     try
213       {
214         jarfile = getJarFile ();
215       }
216     catch (IOException x)
217       {
218         /* ignore */
219       }
220     
221     if (jarfile == null)
222       {
223         JarInputStream zis = new JarInputStream(
224                         jarFileURLConnection.getInputStream ());
225
226         // This is hideous, we're doing a linear search for the thing...
227         for (ZipEntry ent = zis.getNextEntry (); 
228              ent != null; 
229              ent = zis.getNextEntry ())
230           {
231             if (element.equals (ent.getName ()))
232               {
233                 return new JarEntry (ent);
234               }
235           }
236       }
237
238     else
239       {
240         return jarfile.getJarEntry (element);
241       }
242
243     return null;
244   }
245
246   /**
247    * Return the JAR file for this connection
248    *
249    * @exception IOException If an error occurs
250    */
251   public abstract JarFile getJarFile() throws IOException;
252
253
254   // Steal and borrow from protocol/file/Connection.java
255
256   private Hashtable hdrHash = new Hashtable();
257   private Vector hdrVec = new Vector();
258   private boolean gotHeaders = false;
259
260   // Override default method in URLConnection.
261   public String getHeaderField(String name)
262   {
263     try
264       {
265         getHeaders();
266       }
267     catch (IOException x)
268       {
269         return null;
270       }
271     return (String) hdrHash.get(name.toLowerCase());
272   }
273
274   // Override default method in URLConnection.
275   public Map getHeaderFields()
276   {
277     try
278       {
279         getHeaders();
280       }
281     catch (IOException x)
282       {
283         return null;
284       }
285     return hdrHash;
286   }
287
288   // Override default method in URLConnection.
289   public String getHeaderField(int n)
290   {
291     try
292       {
293         getHeaders();
294       }
295     catch (IOException x)
296       {
297         return null;
298       }
299     if (n < hdrVec.size())
300       return getField((String) hdrVec.elementAt(n));
301
302     return null;
303   }
304
305   // Override default method in URLConnection.
306   public String getHeaderFieldKey(int n)
307   {
308     try
309       {
310         getHeaders();
311       }
312     catch (IOException x)
313       {
314         return null;
315       }
316     if (n < hdrVec.size())
317       return getKey((String) hdrVec.elementAt(n));
318
319     return null;
320   }
321
322   private String getKey(String str)
323   {
324     if (str == null)
325       return null;
326     int index = str.indexOf(':');
327     if (index >= 0)
328       return str.substring(0, index);
329     else
330       return null;
331   }
332
333   private String getField(String str)
334   {
335     if (str == null)
336       return null;
337     int index = str.indexOf(':');
338     if (index >= 0)
339       return str.substring(index + 1).trim();
340     else
341       return str;
342   }
343
344   private void getHeaders() throws IOException
345   {
346     if (gotHeaders)
347       return;
348     gotHeaders = true;
349
350     connect();
351
352     // Yes, it is overkill to use the hash table and vector here since
353     // we're only putting one header in the file, but in case we need
354     // to add others later and for consistency, we'll implement it this way.
355
356     // Add the only header we know about right now:  Content-length.
357     long len = -1;
358
359     if (element == null)
360       if (jarFileURLConnection != null)
361         len = jarFileURLConnection.getContentLength ();
362     else
363       {
364         JarEntry entry = getJarEntry();
365         if (entry != null)
366           len = entry.getSize ();
367       }
368
369     String line = "Content-length: " + len;
370     hdrVec.addElement(line);
371
372     // The key will never be null in this scenario since we build up the
373     // headers ourselves.  If we ever rely on getting a header from somewhere
374     // else, then we may have to check if the result of getKey() is null.
375     String key = getKey(line);
376     hdrHash.put(key.toLowerCase(), Long.toString(len));
377   }
378
379   /**
380    * Returns an array of Certificate objects for the jar file entry specified
381    * by this URL or null if there are none
382    *
383    * @return A Certificate array
384    *
385    * @exception IOException If an error occurs
386    */
387   public Certificate[] getCertificates() throws IOException
388   {
389     return getJarEntry().getCertificates();
390   }
391
392   /**
393    * Returns the main Attributes for the JAR file for this connection
394    *
395    * @exception IOException If an error occurs
396    */
397   public Attributes getMainAttributes () throws IOException
398   {
399     return getManifest ().getMainAttributes ();
400   }
401
402   /**
403    * Return the Attributes object for this connection if the URL for it points
404    * to a JAR file entry, null otherwise
405    *
406    * @exception IOException If an error occurs
407    */
408   public Attributes getAttributes () throws IOException
409   {
410     // FIXME: implement this
411     return null;
412   }
413
414   /**
415    * Returns the Manifest for this connection, or null if none
416    *
417    * @exception IOException If an error occurs
418    */
419   public Manifest getManifest () throws IOException
420   {
421     JarFile file = getJarFile ();
422
423     return (file != null) ? file.getManifest() : null;
424   }
425 }