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 */
018 package org.apache.bcel.util;
019
020 import java.io.IOException;
021 import java.io.InputStream;
022 import java.lang.ref.SoftReference;
023 import java.util.HashMap;
024 import java.util.Map;
025 import org.apache.bcel.classfile.ClassParser;
026 import org.apache.bcel.classfile.JavaClass;
027
028 /**
029 * This repository is used in situations where a Class is created
030 * outside the realm of a ClassLoader. Classes are loaded from
031 * the file systems using the paths specified in the given
032 * class path. By default, this is the value returned by
033 * ClassPath.getClassPath().
034 * <br>
035 * It is designed to be used as a singleton, however it
036 * can also be used with custom classpaths.
037 *
038 * @see org.apache.bcel.Repository
039 *
040 * @version $Id: SyntheticRepository.java 1149459 2011-07-22 04:34:27Z dbrosius $
041 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
042 * @author David Dixon-Peugh
043 */
044 public class SyntheticRepository implements Repository {
045
046 private static final long serialVersionUID = 2923440730410019444L;
047 //private static final String DEFAULT_PATH = ClassPath.getClassPath();
048 private static final Map<ClassPath, SyntheticRepository> _instances = new HashMap<ClassPath, SyntheticRepository>(); // CLASSPATH X REPOSITORY
049 private ClassPath _path = null;
050 private Map<String, SoftReference<JavaClass>> _loadedClasses = new HashMap<String, SoftReference<JavaClass>>(); // CLASSNAME X JAVACLASS
051
052
053 private SyntheticRepository(ClassPath path) {
054 _path = path;
055 }
056
057
058 public static SyntheticRepository getInstance() {
059 return getInstance(ClassPath.SYSTEM_CLASS_PATH);
060 }
061
062
063 public static SyntheticRepository getInstance( ClassPath classPath ) {
064 SyntheticRepository rep = _instances.get(classPath);
065 if (rep == null) {
066 rep = new SyntheticRepository(classPath);
067 _instances.put(classPath, rep);
068 }
069 return rep;
070 }
071
072
073 /**
074 * Store a new JavaClass instance into this Repository.
075 */
076 public void storeClass( JavaClass clazz ) {
077 _loadedClasses.put(clazz.getClassName(), new SoftReference<JavaClass>(clazz));
078 clazz.setRepository(this);
079 }
080
081
082 /**
083 * Remove class from repository
084 */
085 public void removeClass( JavaClass clazz ) {
086 _loadedClasses.remove(clazz.getClassName());
087 }
088
089
090 /**
091 * Find an already defined (cached) JavaClass object by name.
092 */
093 public JavaClass findClass( String className ) {
094 SoftReference<JavaClass> ref = _loadedClasses.get(className);
095 if (ref == null) {
096 return null;
097 }
098 return ref.get();
099 }
100
101
102 /**
103 * Find a JavaClass object by name.
104 * If it is already in this Repository, the Repository version
105 * is returned. Otherwise, the Repository's classpath is searched for
106 * the class (and it is added to the Repository if found).
107 *
108 * @param className the name of the class
109 * @return the JavaClass object
110 * @throws ClassNotFoundException if the class is not in the
111 * Repository, and could not be found on the classpath
112 */
113 public JavaClass loadClass( String className ) throws ClassNotFoundException {
114 if (className == null || className.equals("")) {
115 throw new IllegalArgumentException("Invalid class name " + className);
116 }
117 className = className.replace('/', '.'); // Just in case, canonical form
118 JavaClass clazz = findClass(className);
119 if (clazz != null) {
120 return clazz;
121 }
122 try {
123 return loadClass(_path.getInputStream(className), className);
124 } catch (IOException e) {
125 throw new ClassNotFoundException("Exception while looking for class " + className
126 + ": " + e.toString(), e);
127 }
128 }
129
130
131 /**
132 * Find the JavaClass object for a runtime Class object.
133 * If a class with the same name is already in this Repository,
134 * the Repository version is returned. Otherwise, getResourceAsStream()
135 * is called on the Class object to find the class's representation.
136 * If the representation is found, it is added to the Repository.
137 *
138 * @see Class
139 * @param clazz the runtime Class object
140 * @return JavaClass object for given runtime class
141 * @throws ClassNotFoundException if the class is not in the
142 * Repository, and its representation could not be found
143 */
144 public JavaClass loadClass( Class<?> clazz ) throws ClassNotFoundException {
145 InputStream clsStream = null;
146 try{
147 String className = clazz.getName();
148 JavaClass repositoryClass = findClass(className);
149 if (repositoryClass != null) {
150 return repositoryClass;
151 }
152 String name = className;
153 int i = name.lastIndexOf('.');
154 if (i > 0) {
155 name = name.substring(i + 1);
156 }
157 clsStream = clazz.getResourceAsStream(name + ".class");
158 return loadClass(clsStream, className);
159 } finally {
160 try{
161 if (clsStream != null){
162 clsStream.close();
163 }
164 } catch(IOException ioe){
165 //don't care
166 }
167 }
168 }
169
170
171 private JavaClass loadClass( InputStream is, String className ) throws ClassNotFoundException {
172 try {
173 if (is != null) {
174 ClassParser parser = new ClassParser(is, className);
175 JavaClass clazz = parser.parse();
176 storeClass(clazz);
177 return clazz;
178 }
179 } catch (IOException e) {
180 throw new ClassNotFoundException("Exception while looking for class " + className
181 + ": " + e.toString(), e);
182 } finally {
183 if (is != null){
184 try {
185 is.close();
186 } catch (IOException e) {
187 }
188 }
189 }
190 throw new ClassNotFoundException("SyntheticRepository could not load " + className);
191 }
192
193
194 /** ClassPath associated with the Repository.
195 */
196 public ClassPath getClassPath() {
197 return _path;
198 }
199
200
201 /** Clear all entries from cache.
202 */
203 public void clear() {
204 _loadedClasses.clear();
205 }
206 }