1 /*
2  * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * Licensed to the Apache Software Foundation (ASF) under one or more
6  * contributor license agreements.  See the NOTICE file distributed with
7  * this work for additional information regarding copyright ownership.
8  * The ASF licenses this file to You under the Apache License, Version 2.0
9  * (the "License"); you may not use this file except in compliance with
10  * the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 package com.sun.org.apache.bcel.internal.util;
21 
22 import java.io.IOException;
23 import java.io.InputStream;
24 
25 import com.sun.org.apache.bcel.internal.classfile.ClassParser;
26 import com.sun.org.apache.bcel.internal.classfile.JavaClass;
27 import java.lang.ref.SoftReference;
28 import java.util.HashMap;
29 import java.util.Map;
30 
31 /**
32  * This repository is used in situations where a Class is created outside the
33  * realm of a ClassLoader. Classes are loaded from the file systems using the
34  * paths specified in the given class path. By default, this is the value
35  * returned by ClassPath.getClassPath(). <br>
36  * This repository uses a factory design, allowing it to maintain a collection
37  * of different classpaths, and as such It is designed to be used as a singleton
38  * per classpath.
39  *
40  * @see com.sun.org.apache.bcel.internal.Repository
41  *
42  * @version $Id$
43  * @LastModified: Jun 2019
44  */
45 public class SyntheticRepository implements Repository {
46 
47     // CLASSNAME X JAVACLASS
48     private final Map<String, SoftReference<JavaClass>> loadedClasses = new HashMap<>();
49 
SyntheticRepository()50     private SyntheticRepository() {
51     }
52 
getInstance()53     public static SyntheticRepository getInstance() {
54         return new SyntheticRepository();
55     }
56 
57     /**
58      * Store a new JavaClass instance into this Repository.
59      */
60     @Override
storeClass(final JavaClass clazz)61     public void storeClass(final JavaClass clazz) {
62         loadedClasses.put(clazz.getClassName(), new SoftReference<>(clazz));
63         clazz.setRepository(this);
64     }
65 
66     /**
67      * Remove class from repository
68      */
69     @Override
removeClass(final JavaClass clazz)70     public void removeClass(final JavaClass clazz) {
71         loadedClasses.remove(clazz.getClassName());
72     }
73 
74     /**
75      * Find an already defined (cached) JavaClass object by name.
76      */
77     @Override
findClass(final String className)78     public JavaClass findClass(final String className) {
79         final SoftReference<JavaClass> ref = loadedClasses.get(className);
80         if (ref == null) {
81             return null;
82 }
83         return ref.get();
84     }
85 
86     /**
87      * Finds a JavaClass object by name. If it is already in this Repository, the
88      * Repository version is returned.
89      *
90      * @param className the name of the class
91      * @return the JavaClass object
92      * @throws ClassNotFoundException if the class is not in the Repository
93      */
94     @Override
loadClass(String className)95     public JavaClass loadClass(String className) throws ClassNotFoundException {
96         if ((className == null) || className.isEmpty()) {
97             throw new IllegalArgumentException("Invalid class name " + className);
98         }
99         className = className.replace('/', '.'); // Just in case, canonical form
100         final JavaClass clazz = findClass(className);
101         if (clazz != null) {
102             return clazz;
103         }
104 
105         IOException e = new IOException("Couldn't find: " + className + ".class");
106         throw new ClassNotFoundException("Exception while looking for class " +
107                 className + ": " + e, e);
108     }
109 
110     /**
111      * Find the JavaClass object for a runtime Class object. If a class with the
112      * same name is already in this Repository, the Repository version is
113      * returned. Otherwise, getResourceAsStream() is called on the Class object
114      * to find the class's representation. If the representation is found, it is
115      * added to the Repository.
116      *
117      * @see Class
118      * @param clazz the runtime Class object
119      * @return JavaClass object for given runtime class
120      * @throws ClassNotFoundException if the class is not in the Repository, and
121      * its representation could not be found
122      */
123     @Override
loadClass(final Class<?> clazz)124     public JavaClass loadClass(final Class<?> clazz) throws ClassNotFoundException {
125         final String className = clazz.getName();
126         final JavaClass repositoryClass = findClass(className);
127         if (repositoryClass != null) {
128             return repositoryClass;
129         }
130         String name = className;
131         final int i = name.lastIndexOf('.');
132         if (i > 0) {
133             name = name.substring(i + 1);
134         }
135         JavaClass cls = null;
136         try (InputStream clsStream = clazz.getResourceAsStream(name + ".class")) {
137             return cls = loadClass(clsStream, className);
138         } catch (final IOException e) {
139             return cls;
140         }
141 
142     }
143 
144 
loadClass(final InputStream is, final String className)145     private JavaClass loadClass(final InputStream is, final String className)
146             throws ClassNotFoundException {
147         try {
148             if (is != null) {
149                 final ClassParser parser = new ClassParser(is, className);
150                 final JavaClass clazz = parser.parse();
151                 storeClass(clazz);
152                 return clazz;
153             }
154         } catch (final IOException e) {
155             throw new ClassNotFoundException("Exception while looking for class "
156                     + className + ": " + e, e);
157         } finally {
158             if (is != null) {
159                 try {
160                     is.close();
161                 } catch (final IOException e) {
162                     // ignored
163                 }
164             }
165         }
166         throw new ClassNotFoundException("SyntheticRepository could not load "
167                 + className);
168     }
169 
170     /**
171      * Clear all entries from cache.
172      */
173     @Override
clear()174     public void clear() {
175         loadedClasses.clear();
176     }
177 }
178