1 // ========================================================================
2 // Copyright 1996-2005 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 // ========================================================================
14 package org.mortbay.resource;
15 
16 import java.io.File;
17 import java.io.FileOutputStream;
18 import java.io.FilterInputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.net.JarURLConnection;
22 import java.net.URL;
23 import java.util.jar.JarEntry;
24 import java.util.jar.JarInputStream;
25 
26 import org.mortbay.log.Log;
27 import org.mortbay.util.IO;
28 
29 
30 /* ------------------------------------------------------------ */
31 public class JarResource extends URLResource
32 {
33 
34     protected transient JarURLConnection _jarConnection;
35 
36     /* -------------------------------------------------------- */
JarResource(URL url)37     JarResource(URL url)
38     {
39         super(url,null);
40     }
41 
42     /* ------------------------------------------------------------ */
JarResource(URL url, boolean useCaches)43     JarResource(URL url, boolean useCaches)
44     {
45         super(url, null, useCaches);
46     }
47 
48     /* ------------------------------------------------------------ */
release()49     public synchronized void release()
50     {
51         _jarConnection=null;
52         super.release();
53     }
54 
55     /* ------------------------------------------------------------ */
checkConnection()56     protected boolean checkConnection()
57     {
58         super.checkConnection();
59         try
60         {
61             if (_jarConnection!=_connection)
62                 newConnection();
63         }
64         catch(IOException e)
65         {
66             Log.ignore(e);
67             _jarConnection=null;
68         }
69 
70         return _jarConnection!=null;
71     }
72 
73     /* ------------------------------------------------------------ */
74     /**
75      * @throws IOException Sub-classes of <code>JarResource</code> may throw an IOException (or subclass)
76      */
newConnection()77     protected void newConnection() throws IOException
78     {
79         _jarConnection=(JarURLConnection)_connection;
80     }
81 
82     /* ------------------------------------------------------------ */
83     /**
84      * Returns true if the respresenetd resource exists.
85      */
exists()86     public boolean exists()
87     {
88         if (_urlString.endsWith("!/"))
89             return checkConnection();
90         else
91             return super.exists();
92     }
93 
94     /* ------------------------------------------------------------ */
getFile()95     public File getFile()
96         throws IOException
97     {
98         return null;
99     }
100 
101     /* ------------------------------------------------------------ */
getInputStream()102     public InputStream getInputStream()
103         throws java.io.IOException
104     {
105         checkConnection();
106         if (!_urlString.endsWith("!/"))
107             return new FilterInputStream(super.getInputStream())
108             {
109                 public void close() throws IOException {this.in=IO.getClosedStream();}
110             };
111 
112         URL url = new URL(_urlString.substring(4,_urlString.length()-2));
113         InputStream is = url.openStream();
114         return is;
115     }
116 
117     /* ------------------------------------------------------------ */
118     public static void extract(Resource resource, File directory, boolean deleteOnExit)
119         throws IOException
120     {
121         if(Log.isDebugEnabled())Log.debug("Extract "+resource+" to "+directory);
122 
123 
124         String urlString = resource.getURL().toExternalForm().trim();
125         int endOfJarUrl = urlString.indexOf("!/");
126         int startOfJarUrl = (endOfJarUrl >= 0?4:0);
127 
128         if (endOfJarUrl < 0)
129             throw new IOException("Not a valid jar url: "+urlString);
130 
131         URL jarFileURL = new URL(urlString.substring(startOfJarUrl, endOfJarUrl));
132         String subEntryName = (endOfJarUrl+2 < urlString.length() ? urlString.substring(endOfJarUrl + 2) : null);
133         boolean subEntryIsDir = (subEntryName != null && subEntryName.endsWith("/")?true:false);
134 
135         if (Log.isDebugEnabled()) Log.debug("Extracting entry = "+subEntryName+" from jar "+jarFileURL);
136 
137         InputStream is = jarFileURL.openConnection().getInputStream();
138         JarInputStream jin = new JarInputStream(is);
139         JarEntry entry;
140         boolean shouldExtract;
141         while((entry=jin.getNextJarEntry())!=null)
142         {
143             String entryName = entry.getName();
144 
145             if ((subEntryName != null) && (entryName.startsWith(subEntryName)))
146             {
147                 //if there is a particular subEntry that we are looking for, only
148                 //extract it.
149                 if (subEntryIsDir)
150                 {
151                     //if it is a subdirectory we are looking for, then we
152                     //are looking to extract its contents into the target
153                     //directory. Remove the name of the subdirectory so
154                     //that we don't wind up creating it too.
155                     entryName = entryName.substring(subEntryName.length());
156                     if (!entryName.equals(""))
157                     {
158                         //the entry is
159                         shouldExtract = true;
160                     }
161                     else
162                         shouldExtract = false;
163                 }
164                 else
165                     shouldExtract = true;
166             }
167             else if ((subEntryName != null) && (!entryName.startsWith(subEntryName)))
168             {
169                 //there is a particular entry we are looking for, and this one
170                 //isn't it
171                 shouldExtract = false;
172             }
173             else
174             {
175                 //we are extracting everything
176                 shouldExtract =  true;
177             }
178 
179 
180             if (!shouldExtract)
181             {
182                 if (Log.isDebugEnabled()) Log.debug("Skipping entry: "+entryName);
183                 continue;
184             }
185 
186 
187             File file=new File(directory,entryName);
188             if (entry.isDirectory())
189             {
190                 // Make directory
191                 if (!file.exists())
192                     file.mkdirs();
193             }
194             else
195             {
196                 // make directory (some jars don't list dirs)
197                 File dir = new File(file.getParent());
198                 if (!dir.exists())
199                     dir.mkdirs();
200 
201                 // Make file
202                 FileOutputStream fout = null;
203                 try
204                 {
205                     fout = new FileOutputStream(file);
206                     IO.copy(jin,fout);
207                 }
208                 finally
209                 {
210                     IO.close(fout);
211                 }
212 
213                 // touch the file.
214                 if (entry.getTime()>=0)
215                     file.setLastModified(entry.getTime());
216             }
217             if (deleteOnExit)
218                 file.deleteOnExit();
219         }
220         IO.close(jin);
221     }
222 
223     /* ------------------------------------------------------------ */
224     public void extract(File directory, boolean deleteOnExit)
225         throws IOException
226     {
227         extract(this,directory,deleteOnExit);
228     }
229 }
230