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 }