Coverage Report - org.apache.myfaces.view.facelets.util.Classpath
 
Classes in this File Line Coverage Branch Coverage Complexity
Classpath
0%
0/128
0%
0/80
4.5
Classpath$1
0%
0/2
N/A
4.5
 
 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  
 package org.apache.myfaces.view.facelets.util;
 20  
 
 21  
 import java.io.File;
 22  
 import java.io.IOException;
 23  
 import java.io.InputStream;
 24  
 import java.net.JarURLConnection;
 25  
 import java.net.URL;
 26  
 import java.net.URLConnection;
 27  
 import java.net.URLDecoder;
 28  
 import java.util.Arrays;
 29  
 import java.util.Enumeration;
 30  
 import java.util.HashSet;
 31  
 import java.util.LinkedHashSet;
 32  
 import java.util.Set;
 33  
 import java.util.jar.JarEntry;
 34  
 import java.util.jar.JarFile;
 35  
 import java.util.zip.ZipEntry;
 36  
 import java.util.zip.ZipInputStream;
 37  
 
 38  
 import org.apache.myfaces.shared.util.ClassUtils;
 39  
 import java.nio.ByteBuffer;
 40  
 import java.nio.charset.Charset;
 41  
 import java.security.AccessController;
 42  
 import java.security.PrivilegedAction;
 43  
 /**
 44  
  * @author Jacob Hookom
 45  
  * @author Roland Huss
 46  
  * @author Ales Justin (ales.justin@jboss.org)
 47  
  * @version $Id$
 48  
  */
 49  
 public final class Classpath
 50  
 {
 51  0
     private static final Charset UTF8 = Charset.forName("UTF-8");
 52  0
     private static final Set<String> EXCLUDED_PREFIX_SET = new HashSet<String>(Arrays.asList("rar:", "sar:"));
 53  0
     private static final Set<String> EXCLUDED_SUFFIX_SET = new HashSet<String>(Arrays.asList(".rar", ".sar"));
 54  
 
 55  
     private Classpath()
 56  0
     {
 57  0
     }
 58  
 
 59  
     public static URL[] search(String prefix, String suffix) throws IOException
 60  
     {
 61  0
         return search(ClassUtils.getContextClassLoader(), prefix, suffix);
 62  
     }
 63  
 
 64  
     public static URL[] search(ClassLoader loader, String prefix, String suffix) throws IOException
 65  
     {
 66  0
         Set<URL> all = new LinkedHashSet<URL>();
 67  
 
 68  0
         _searchResource(all, loader, prefix, prefix, suffix);
 69  0
         _searchResource(all, loader, prefix + "MANIFEST.MF", prefix, suffix);
 70  
 
 71  0
         URL[] urlArray = (URL[]) all.toArray(new URL[all.size()]);
 72  
 
 73  0
         return urlArray;
 74  
     }
 75  
 
 76  
     private static void _searchResource(Set<URL> result, ClassLoader loader, String resource, String prefix,
 77  
                                         String suffix) throws IOException
 78  
     {
 79  0
         for (Enumeration<URL> urls = loader.getResources(resource); urls.hasMoreElements();)
 80  
         {
 81  0
             URL url = urls.nextElement();
 82  0
             URLConnection conn = url.openConnection();
 83  0
             conn.setUseCaches(false);
 84  0
             conn.setDefaultUseCaches(false);
 85  
 
 86  0
             JarFile jar = null;
 87  
             try 
 88  
             {
 89  0
                 if (conn instanceof JarURLConnection)
 90  
                 {
 91  0
                         jar = ((JarURLConnection) conn).getJarFile();
 92  
                 }
 93  
                 else
 94  
                 {
 95  0
                     jar = _getAlternativeJarFile(url);
 96  
                 }
 97  
     
 98  0
                 if (jar != null)
 99  
                 {
 100  0
                     _searchJar(loader, result, jar, prefix, suffix);
 101  
                 }
 102  
                 else
 103  
                 {
 104  0
                     if (!_searchDir(result, new File(URLDecoder.decode(url.getFile(), "UTF-8")), suffix))
 105  
                     {
 106  0
                         _searchFromURL(result, prefix, suffix, url);
 107  
                     }
 108  
                 }    
 109  
             }
 110  0
             catch (Throwable e)
 111  
             {
 112  
                 // This can happen if the classloader provided us a URL that it thinks exists
 113  
                 // but really doesn't.  In particular, if a JAR contains META-INF/MANIFEST.MF
 114  
                 // but not META-INF/, some classloaders may incorrectly report that META-INF/
 115  
                 // exists and we'll end up here.  Just ignore this case.
 116  
                 
 117  
                 continue;
 118  
             }
 119  
             finally 
 120  
             {
 121  0
                 if (jar != null) 
 122  
                 {
 123  0
                     jar.close();
 124  
                 }
 125  
             }
 126  0
         }
 127  0
     }
 128  
 
 129  
     private static boolean _searchDir(Set<URL> result, File dir, String suffix) throws IOException
 130  
     {
 131  0
         boolean dirExists = false;
 132  0
         if (System.getSecurityManager() != null)
 133  
         {
 134  0
             final File finalDir = dir;
 135  0
             dirExists = (Boolean) AccessController.doPrivileged(new PrivilegedAction()
 136  0
             {
 137  
                 public Object run() 
 138  
                 {
 139  0
                     return finalDir.exists();
 140  
                 }
 141  
             });
 142  0
         }  
 143  
         else
 144  
         {
 145  0
             dirExists = dir.exists();
 146  
         }
 147  0
         if (dirExists && dir.isDirectory())
 148  
         {
 149  0
             File[] dirFiles = dir.listFiles();
 150  0
             if (dirFiles != null) 
 151  
             {
 152  0
                 for (File file : dirFiles)
 153  
                 {
 154  0
                     String path = file.getAbsolutePath();
 155  0
                     if (file.isDirectory())
 156  
                     {
 157  0
                         _searchDir(result, file, suffix);
 158  
                     }
 159  0
                     else if (path.endsWith(suffix))
 160  
                     {
 161  0
                         result.add(file.toURI().toURL());
 162  
                     }
 163  
                 }
 164  0
                 return true;
 165  
             }
 166  
         }
 167  
 
 168  0
         return false;
 169  
     }
 170  
 
 171  
     /**
 172  
      * Search from URL. Fall back on prefix tokens if not able to read from original url param.
 173  
      * 
 174  
      * @param result
 175  
      *            the result urls
 176  
      * @param prefix
 177  
      *            the current prefix
 178  
      * @param suffix
 179  
      *            the suffix to match
 180  
      * @param url
 181  
      *            the current url to start search
 182  
      * @throws IOException
 183  
      *             for any error
 184  
      */
 185  
     private static void _searchFromURL(Set<URL> result, String prefix, String suffix, URL url) throws IOException
 186  
     {
 187  0
         boolean done = false;
 188  
 
 189  0
         InputStream is = _getInputStream(url);
 190  0
         if (is != null)
 191  
         {
 192  
             try
 193  
             {
 194  
                 ZipInputStream zis;
 195  0
                 if (is instanceof ZipInputStream)
 196  
                 {
 197  0
                     zis = (ZipInputStream) is;
 198  
                 }
 199  
                 else
 200  
                 {
 201  0
                     zis = new ZipInputStream(is);
 202  
                 }
 203  
 
 204  
                 try
 205  
                 {
 206  0
                     ZipEntry entry = zis.getNextEntry();
 207  
                     // initial entry should not be null
 208  
                     // if we assume this is some inner jar
 209  0
                     done = entry != null;
 210  
 
 211  0
                     while (entry != null)
 212  
                     {
 213  0
                         String entryName = entry.getName();
 214  0
                         if (entryName.endsWith(suffix))
 215  
                         {
 216  0
                             result.add(new URL(url.toExternalForm() + entryName));
 217  
                         }
 218  
 
 219  0
                         entry = zis.getNextEntry();
 220  0
                     }
 221  
                 }
 222  
                 finally
 223  
                 {
 224  0
                     zis.close();
 225  0
                 }
 226  
             }
 227  0
             catch (Exception ignore)
 228  
             {
 229  0
             }
 230  
         }
 231  
 
 232  0
         if (!done && prefix.length() > 0)
 233  
         {
 234  
             // we add '/' at the end since join adds it as well
 235  0
             String urlString = url.toExternalForm() + "/";
 236  
 
 237  0
             String[] split = prefix.split("/");
 238  
 
 239  0
             prefix = _join(split, true);
 240  
 
 241  0
             String end = _join(split, false);
 242  0
             urlString = urlString.substring(0, urlString.lastIndexOf(end));
 243  0
             if (isExcludedPrefix(urlString))
 244  
             {
 245  
                 // excluded URL found, ignore it
 246  0
                 return;
 247  
             }
 248  0
             url = new URL(urlString);
 249  
 
 250  0
             _searchFromURL(result, prefix, suffix, url);
 251  
         }
 252  0
     }
 253  
 
 254  
     /**
 255  
      * Join tokens, exlude last if param equals true.
 256  
      * 
 257  
      * @param tokens
 258  
      *            the tokens
 259  
      * @param excludeLast
 260  
      *            do we exclude last token
 261  
      * @return joined tokens
 262  
      */
 263  
     private static String _join(String[] tokens, boolean excludeLast)
 264  
     {
 265  0
         StringBuilder join = new StringBuilder();
 266  0
         int length = tokens.length - (excludeLast ? 1 : 0);
 267  0
         for (int i = 0; i < length; i++)
 268  
         {
 269  0
             join.append(tokens[i]).append("/");
 270  
         }
 271  
 
 272  0
         return join.toString();
 273  
     }
 274  
 
 275  
     /**
 276  
      * Open input stream from url. Ignore any errors.
 277  
      * 
 278  
      * @param url
 279  
      *            the url to open
 280  
      * @return input stream or null if not possible
 281  
      */
 282  
     private static InputStream _getInputStream(URL url)
 283  
     {
 284  
         try
 285  
         {
 286  0
             return url.openStream();
 287  
         }
 288  0
         catch (Throwable t)
 289  
         {
 290  0
             return null;
 291  
         }
 292  
     }
 293  
 
 294  
     /**
 295  
      * For URLs to JARs that do not use JarURLConnection - allowed by the servlet spec - attempt to produce a JarFile
 296  
      * object all the same. Known servlet engines that function like this include Weblogic and OC4J. This is not a full
 297  
      * solution, since an unpacked WAR or EAR will not have JAR "files" as such.
 298  
      */
 299  
     private static JarFile _getAlternativeJarFile(URL url) throws IOException
 300  
     {
 301  0
         String urlFile = url.getFile();
 302  
 
 303  
         // Find suffix prefixed by "!/" on Weblogic
 304  0
         int wlIndex = urlFile.indexOf("!/");
 305  
         // Find suffix prefixed by '!' on OC4J
 306  0
         int oc4jIndex = urlFile.indexOf('!');
 307  
         // Take the first found suffix
 308  0
         int separatorIndex = wlIndex == -1 && oc4jIndex == -1 ? -1 : wlIndex < oc4jIndex ? wlIndex : oc4jIndex;
 309  
 
 310  0
         if (separatorIndex != -1)
 311  
         {
 312  0
             String jarFileUrl = urlFile.substring(0, separatorIndex);
 313  
             // And trim off any "file:" prefix.
 314  0
             if (jarFileUrl.startsWith("file:"))
 315  
             {
 316  0
                 jarFileUrl = jarFileUrl.substring("file:".length());
 317  
             }
 318  
             // make sure this is a valid file system path by removing escaping of white-space characters, etc. 
 319  0
             jarFileUrl = decodeFilesystemUrl(jarFileUrl);
 320  0
             if (isExcludedPrefix(jarFileUrl) || isExcludedSuffix(jarFileUrl))
 321  
             {
 322  
                 // excluded URL found, ignore it
 323  0
                 return null;
 324  
             }
 325  0
             return new JarFile(jarFileUrl);
 326  
         }
 327  
 
 328  0
         return null;
 329  
     }
 330  
 
 331  
     private static boolean isExcludedPrefix(String url)
 332  
     {
 333  0
         return EXCLUDED_PREFIX_SET.contains(url.substring(0, 4));
 334  
     }
 335  
 
 336  
     private static boolean isExcludedSuffix(String url)
 337  
     {
 338  0
         int length = url.length();
 339  0
         return EXCLUDED_SUFFIX_SET.contains(url.substring(length - 4, length));
 340  
     }
 341  
 
 342  
     private static void _searchJar(ClassLoader loader, Set<URL> result, JarFile file, String prefix, String suffix)
 343  
             throws IOException
 344  
     {
 345  0
         Enumeration<JarEntry> e = file.entries();
 346  0
         while (e.hasMoreElements())
 347  
         {
 348  
             try
 349  
             {
 350  0
                 String name = e.nextElement().getName();
 351  0
                 if (name.startsWith(prefix) && name.endsWith(suffix))
 352  
                 {
 353  0
                     Enumeration<URL> e2 = loader.getResources(name);
 354  0
                     while (e2.hasMoreElements())
 355  
                     {
 356  0
                         result.add(e2.nextElement());
 357  
                     }
 358  
                 }
 359  
             }
 360  0
             catch (Throwable t)
 361  
             {
 362  
                 // shallow
 363  0
             }
 364  
         }
 365  0
     }
 366  
 
 367  
     private static String decodeFilesystemUrl(String url)
 368  
     {
 369  
         //borrowed from commons-io FileUtils.
 370  0
         String decoded = url;
 371  0
         if (url != null && url.indexOf('%') >= 0)
 372  
         {
 373  0
             int n = url.length();
 374  0
             StringBuffer buffer = new StringBuffer();
 375  0
             ByteBuffer bytes = ByteBuffer.allocate(n);
 376  0
             for (int i = 0; i < n; )
 377  
             {
 378  0
                 if (url.charAt(i) == '%')
 379  
                 {
 380  
                     try
 381  
                     {
 382  
                         do
 383  
                         {
 384  0
                             byte octet = (byte) Integer.parseInt(url.substring(i + 1, i + 3), 16);
 385  0
                             bytes.put(octet);
 386  0
                             i += 3;
 387  0
                         } while (i < n && url.charAt(i) == '%');
 388  
                         continue;
 389  
                     }
 390  0
                     catch (RuntimeException e)
 391  
                     {
 392  
                         // malformed percent-encoded octet, fall through and
 393  
                         // append characters literally
 394  
                     }
 395  
                     finally
 396  
                     {
 397  0
                         if (bytes.position() > 0)
 398  
                         {
 399  0
                             bytes.flip();
 400  0
                             buffer.append(UTF8.decode(bytes).toString());
 401  0
                             bytes.clear();
 402  
                         }
 403  
                     }
 404  
                 }
 405  0
                 buffer.append(url.charAt(i++));
 406  
             }
 407  0
             decoded = buffer.toString();
 408  
         }
 409  0
         return decoded;
 410  
     }
 411  
 
 412  
 }