View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.shared.resource;
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.net.JarURLConnection;
25  import java.net.URISyntaxException;
26  import java.net.URL;
27  import java.util.Collections;
28  import java.util.Deque;
29  import java.util.Enumeration;
30  import java.util.Iterator;
31  import java.util.LinkedList;
32  import java.util.jar.JarEntry;
33  import java.util.jar.JarFile;
34  import java.util.logging.Level;
35  import java.util.logging.Logger;
36  import javax.faces.application.ResourceVisitOption;
37  import org.apache.myfaces.shared.util.ClassUtils;
38  
39  /**
40   *
41   * @author lu4242
42   */
43  public class ClassLoaderResourceLoaderIterator implements Iterator<String>
44  {
45      private Iterator<String> delegate = null;
46  
47      public ClassLoaderResourceLoaderIterator(URL url, String basePath, 
48              int maxDepth, ResourceVisitOption... options)
49      {
50          if (url == null)
51          {
52              // This library does not exists for this
53              // ResourceLoader
54              delegate = null;
55          }
56          else
57          {
58              if (url.getProtocol().equals("file"))
59              {
60                  try
61                  {
62                      File directory = new File(url.toURI());
63                      delegate = new FileDepthIterator(directory, basePath, maxDepth, options);
64                  }
65                  catch (URISyntaxException e)
66                  {
67                      Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName()); 
68                      if (log.isLoggable(Level.WARNING))
69                      {
70                          log.log(Level.WARNING, "url "+url.toString()+" cannot be translated to uri: "
71                                  +e.getMessage(), e);
72                      }
73                  }
74              }
75              else if (isJarResourceProtocol(url.getProtocol()))
76              {
77                  url = getClassLoader().getResource(basePath);
78  
79                  if (url != null)
80                  {
81                      delegate = new JarDepthIterator(url, basePath, maxDepth, options);
82                  }
83              }
84          }
85      }
86  
87      @Override
88      public boolean hasNext()
89      {
90          if (delegate != null)
91          {
92              return delegate.hasNext();
93          }
94          return false;
95      }
96  
97      @Override
98      public String next()
99      {
100         if (delegate != null)
101         {
102             return delegate.next();
103         }
104         return null;
105     }
106 
107     @Override
108     public void remove()
109     {
110         //No op
111     }
112     
113     protected ClassLoader getClassLoader()
114     {
115         return ClassUtils.getContextClassLoader();
116     }
117  
118     private static class JarDepthIterator implements Iterator<String>
119     {
120         private URL directory;
121         private String basePath;
122         private int maxDepth;
123         private ResourceVisitOption[] options;
124         
125         private Deque<String> stack = new LinkedList<String>();
126         
127         Iterator<String> iterator = null;
128 
129         public JarDepthIterator(URL directory, String basePath, int maxDepth, ResourceVisitOption... options)
130         {
131             this.directory = directory;
132             this.basePath = basePath;
133             this.maxDepth = maxDepth;
134             this.options = options;
135             
136             if (basePath.endsWith("/"))
137             {
138                 basePath = basePath.substring(0, basePath.length()-1);
139             }
140 
141             try
142             {
143                 JarURLConnection conn = (JarURLConnection)directory.openConnection();
144                 // See DIGESTER-29 for related problem
145                 conn.setUseCaches(false);
146 
147                 try
148                 {
149                     if (conn.getJarEntry().isDirectory())
150                     {
151                         // Unfortunately, we have to scan all entry files
152                         JarFile file = conn.getJarFile();
153                         for (Enumeration<JarEntry> en = file.entries(); en.hasMoreElements();)
154                         {
155                             JarEntry entry = en.nextElement();
156                             String entryName = entry.getName();
157                             String path;
158 
159                             if (entryName.startsWith(basePath + '/'))
160                             {
161                                 if (entryName.length() == basePath.length() + 1)
162                                 {
163                                     // the same string, just skip it
164                                     continue;
165                                 }
166                                 
167                                 path = entryName.substring(basePath.length(), entryName.length());
168     
169                                 if (path.endsWith("/"))
170                                 {
171                                     // Inner Directory
172                                     continue;
173                                 }
174 
175                                 //TODO: scan listFiles
176                                 int depth = ResourceLoaderUtils.getDepth(path);
177                                 if (depth < maxDepth)
178                                 {
179                                     stack.add(path);
180                                 }
181                             }
182                         }
183                     }
184                 }
185                 finally
186                 {
187                     //See TRINIDAD-73
188                     //just close the input stream again if
189                     //by inspecting the entries the stream
190                     //was let open.
191                     try
192                     {
193                         conn.getInputStream().close();
194                     }
195                     catch (Exception exception)
196                     {
197                         // Ignored
198                     }
199                 }
200             }
201             catch (IOException e)
202             {
203                 // Just return null, because library version cannot be
204                 // resolved.
205                 Logger log = Logger.getLogger(ClassLoaderResourceLoader.class.getName()); 
206                 if (log.isLoggable(Level.WARNING))
207                 {
208                     log.log(Level.WARNING, "IOException when scanning for resource in jar file:", e);
209                 }
210             }
211             iterator = stack.iterator();
212         }
213 
214         @Override
215         public boolean hasNext()
216         {
217             return iterator.hasNext();
218         }
219 
220         @Override
221         public String next()
222         {
223             return iterator.next();
224         }
225 
226         @Override
227         public void remove()
228         {
229             //No op
230         }
231     }
232     
233     private static class FileDepthIterator implements Iterator<String>
234     {
235         private File directory;
236         private String basePath;
237         private int maxDepth;
238         private ResourceVisitOption[] options;
239         
240         private Deque<File> stack = new LinkedList<File>();
241         private String basePathName;
242 
243         public FileDepthIterator(File directory, String basePath, int maxDepth, ResourceVisitOption... options)
244         {
245             this.directory = directory;
246             this.basePath = basePath;
247             this.maxDepth = maxDepth;
248             this.options = options;
249             
250             File[] list = this.directory.listFiles();
251             Collections.addAll(stack, list);
252 
253             this.basePathName = this.directory.getPath().replace(File.separatorChar, '/');
254         }
255         
256         @Override
257         public boolean hasNext()
258         {
259             if (!stack.isEmpty())
260             {
261                 File file = stack.peek();
262                 do 
263                 {
264                     if (file.isDirectory())
265                     {
266                         file = stack.pop();
267                         int depth = ResourceLoaderUtils.getDepth(calculatePath(file));
268                         if (depth < maxDepth)
269                         {
270                             File[] list = file.listFiles();
271                             for (File f : list)
272                             {
273                                 stack.add(f);
274                             }
275                         }
276                         if (!stack.isEmpty())
277                         {
278                             file = stack.peek();
279                         }
280                         else
281                         {
282                             file = null;
283                         }
284                     }
285                 }
286                 while (file != null && file.isDirectory() && !stack.isEmpty());
287                 
288                 return !stack.isEmpty();
289             }
290             return false;
291         }
292 
293         @Override
294         public String next()
295         {
296             if (!stack.isEmpty())
297             {
298                 File file = stack.pop();
299                 do 
300                 {
301                     if (file.isDirectory())
302                     {
303                         int depth = ResourceLoaderUtils.getDepth(calculatePath(file));
304                         if (depth < maxDepth)
305                         {
306                             File[] list = file.listFiles();
307                             for (File f : list)
308                             {
309                                 stack.add(f);
310                             }
311                         }
312                         if (!stack.isEmpty())
313                         {
314                             file = stack.pop();
315                         }
316                         else
317                         {
318                             file = null;
319                         }
320                     }
321                 }
322                 while (file != null && file.isDirectory() && !stack.isEmpty());
323                 if (file != null)
324                 {
325                     // Calculate name based on url, basePath.
326                     String path = calculatePath(file);
327                     return path;
328                 }
329             }
330             return null;
331         }
332         
333         private String calculatePath(File file)
334         {
335             return (file.getPath()).substring(this.basePathName.length()).replace(File.separatorChar, '/');
336         }
337 
338         @Override
339         public void remove()
340         {
341             //No op
342         }
343     }
344 
345     /**
346      * <p>Determines whether the given URL resource protocol refers to a JAR file. Note that
347      * BEA WebLogic and IBM WebSphere don't use the "jar://" protocol for some reason even
348      * though you can treat these resources just like normal JAR files, i.e. you can ignore
349      * the difference between these protocols after this method has returned.</p>
350      *
351      * @param protocol the URL resource protocol you want to check
352      *
353      * @return <code>true</code> if the given URL resource protocol refers to a JAR file,
354      *          <code>false</code> otherwise
355      */
356     private static boolean isJarResourceProtocol(String protocol)
357     {
358     // Websphere uses the protocol "wsjar://" and Weblogic uses the protocol "zip://".
359     return "jar".equals(protocol) || "wsjar".equals(protocol) || "zip".equals(protocol); 
360     }
361 }