001 /* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014 package org.apache.commons.classscan.builtin; 015 016 import java.io.IOException; 017 import java.net.JarURLConnection; 018 import java.net.MalformedURLException; 019 import java.net.URI; 020 import java.net.URISyntaxException; 021 import java.net.URL; 022 import java.util.ArrayList; 023 import java.util.Collection; 024 import java.util.Enumeration; 025 import java.util.Iterator; 026 import java.util.List; 027 import java.util.jar.Attributes; 028 import java.util.jar.JarEntry; 029 import java.util.jar.JarFile; 030 import java.util.jar.Manifest; 031 import java.util.regex.Pattern; 032 033 import org.apache.commons.classscan.ClassFile; 034 import org.apache.commons.classscan.ResourceFile; 035 import org.apache.commons.classscan.spi.model.SpiClassPathElement; 036 import org.apache.commons.classscan.spi.model.SpiMetaRegistry; 037 import org.slf4j.Logger; 038 import org.slf4j.LoggerFactory; 039 040 public class JarClassPathElement implements SpiClassPathElement { 041 042 private static final Logger logger = LoggerFactory.getLogger(JarClassPathElement.class); 043 044 final private URL location; 045 private URL[] classPathLocations; 046 047 public JarClassPathElement(URL location) { 048 this.location = location; 049 } 050 051 @Override 052 public Iterator<ClassFile> iterator() { 053 final JarFile jarFile= getJarFile(); 054 final Enumeration<JarEntry> jarEntries = jarFile.entries(); 055 056 return new ClassFileIterator() { 057 ClassFile getNext() { 058 while(jarEntries.hasMoreElements()) { 059 JarClassFile jcf= new JarClassFile(jarFile, jarEntries.nextElement()); 060 if(jcf.getClassName()!=null) { 061 return jcf; 062 } 063 } 064 try { 065 jarFile.close(); 066 } catch (IOException ex) { 067 logger.warn("Failed to close " + location, ex); 068 } 069 return null; 070 } 071 }; 072 } 073 074 private JarFile getJarFile() { 075 try { 076 JarURLConnection conn = (JarURLConnection) location.openConnection(); 077 JarFile jf= conn.getJarFile(); 078 extractClasspath(jf); 079 return jf; 080 } 081 catch (IOException ex) { 082 logger.warn("Failed to open " + location, ex); 083 return null; 084 } 085 } 086 087 private void extractClasspath(JarFile jf) throws IOException { 088 Manifest manifest = jf.getManifest(); 089 if(manifest == null) { 090 return; 091 } 092 093 URI resolveLocation; 094 try { 095 resolveLocation = location.toURI(); 096 } catch (URISyntaxException e) { 097 logger.warn("Could not create URI out of {}", location); 098 return; 099 } 100 101 Attributes attributes = manifest.getMainAttributes(); 102 String classPath = attributes.getValue(Attributes.Name.CLASS_PATH); 103 if (classPath == null) { 104 return; 105 } 106 107 List<URL> newLocations = new ArrayList<URL>(); 108 for (String pathElement : PATH_SPLITTER.split(classPath)) { 109 URI resolvedLocation = resolveLocation.resolve(pathElement); 110 try { 111 URL newLocation = resolvedLocation.toURL(); 112 newLocations.add(newLocation); 113 } catch (MalformedURLException e) { 114 logger.warn("Could not resolve Class-Path element {} relative to {}", pathElement, resolveLocation); 115 } 116 } 117 classPathLocations = newLocations.toArray(new URL[newLocations.size()]); 118 } 119 120 private static Pattern PATH_SPLITTER = Pattern.compile("\\s+"); 121 122 @Override 123 public String getLocation() { 124 return location.toExternalForm(); 125 } 126 127 @Override 128 public Collection<SpiClassPathElement> getAdditionalLocations(SpiMetaRegistry registry) { 129 if(classPathLocations==null) { 130 return null; 131 } 132 return UrlClassPath.getClassPathElements(registry, classPathLocations); 133 } 134 135 @Override 136 public ResourceFile getResource(String fileName) { 137 JarFile jarFile= getJarFile(); 138 JarEntry jarEntry = jarFile.getJarEntry(fileName); 139 if(jarEntry==null) { 140 try { 141 jarFile.close(); 142 } catch (IOException e) { 143 logger.info("ignoring exception on close", e); 144 } 145 return null; 146 } 147 return new JarResourceFile(jarFile, jarEntry); 148 } 149 }