001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.xbean.finder.archive;
018
019 import java.io.File;
020 import java.io.IOException;
021 import java.io.InputStream;
022 import java.net.URL;
023 import java.util.ArrayList;
024 import java.util.Enumeration;
025 import java.util.Iterator;
026 import java.util.List;
027 import java.util.jar.JarEntry;
028 import java.util.jar.JarFile;
029 import java.util.jar.JarInputStream;
030
031 /**
032 * @version $Rev$ $Date$
033 */
034 public class JarArchive implements Archive {
035
036 private final ClassLoader loader;
037 private final URL url;
038
039 public JarArchive(ClassLoader loader, URL url) {
040 if (!"jar".equals(url.getProtocol())) throw new IllegalArgumentException("not a file url: " + url);
041 this.loader = loader;
042 this.url = url;
043 }
044
045 @Override
046 public InputStream getBytecode(String className) throws IOException, ClassNotFoundException {
047 int pos = className.indexOf("<");
048 if (pos > -1) {
049 className = className.substring(0, pos);
050 }
051 pos = className.indexOf(">");
052 if (pos > -1) {
053 className = className.substring(0, pos);
054 }
055 if (!className.endsWith(".class")) {
056 className = className.replace('.', '/') + ".class";
057 }
058
059 URL resource = loader.getResource(className);
060 if (resource != null) return resource.openStream();
061
062 throw new ClassNotFoundException(className);
063 }
064
065
066 @Override
067 public Class<?> loadClass(String className) throws ClassNotFoundException {
068 return loader.loadClass(className);
069 }
070
071 @Override
072 public Iterator<String> iterator() {
073 try {
074 return jar(url).iterator();
075 } catch (IOException e) {
076 throw new IllegalStateException(e);
077 }
078 }
079
080 private List<String> jar(URL location) throws IOException {
081 String jarPath = location.getFile();
082 if (jarPath.indexOf("!") > -1){
083 jarPath = jarPath.substring(0, jarPath.indexOf("!"));
084 }
085 URL url = new URL(jarPath);
086 if ("file".equals(url.getProtocol())) { // ZipFile is faster than ZipInputStream
087 JarFile jarFile = new JarFile(url.getFile().replace("%20", " "));
088 return jar(jarFile);
089 } else {
090 InputStream in = url.openStream();
091 try {
092 JarInputStream jarStream = new JarInputStream(in);
093 return jar(jarStream);
094 } finally {
095 in.close();
096 }
097 }
098 }
099
100 private List<String> jar(JarFile jarFile) {
101 List<String> classNames = new ArrayList<String>();
102
103 Enumeration<? extends JarEntry> jarEntries =jarFile.entries();
104 while (jarEntries.hasMoreElements()) {
105 JarEntry entry = jarEntries.nextElement();
106 addClassName(classNames, entry);
107 }
108
109 return classNames;
110 }
111
112 private List<String> jar(JarInputStream jarStream) throws IOException {
113 List<String> classNames = new ArrayList<String>();
114
115 JarEntry entry;
116 while ((entry = jarStream.getNextJarEntry()) != null) {
117 addClassName(classNames, entry);
118 }
119
120 return classNames;
121 }
122
123 private void addClassName(List<String> classNames, JarEntry entry) {
124 if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
125 return;
126 }
127 String className = entry.getName();
128 className = className.replaceFirst(".class$", "");
129 if (className.contains(".")) {
130 return;
131 }
132 className = className.replace(File.separatorChar, '.');
133 classNames.add(className);
134 }
135 }
136