1 /*
2  * Copyright (c) 2017, 2020, 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  * @LastModified: Jan 2020
43  */
44 public class SyntheticRepository implements Repository {
45 
46     // CLASSNAME X JAVACLASS
47     private final Map<String, SoftReference<JavaClass>> loadedClasses = new HashMap<>();
48 
SyntheticRepository()49     private SyntheticRepository() {
50     }
51 
getInstance()52     public static SyntheticRepository getInstance() {
53         return new SyntheticRepository();
54     }
55 
56     /**
57      * Store a new JavaClass instance into this Repository.
58      */
59     @Override
storeClass(final JavaClass clazz)60     public void storeClass(final JavaClass clazz) {
61         loadedClasses.put(clazz.getClassName(), new SoftReference<>(clazz));
62         clazz.setRepository(this);
63     }
64 
65     /**
66      * Remove class from repository
67      */
68     @Override
removeClass(final JavaClass clazz)69     public void removeClass(final JavaClass clazz) {
70         loadedClasses.remove(clazz.getClassName());
71     }
72 
73     /**
74      * Find an already defined (cached) JavaClass object by name.
75      */
76     @Override
findClass(final String className)77     public JavaClass findClass(final String className) {
78         final SoftReference<JavaClass> ref = loadedClasses.get(className);
79         if (ref == null) {
80             return null;
81         }
82         return ref.get();
83     }
84 
85     /**
86      * Finds a JavaClass object by name. If it is already in this Repository, the
87      * Repository version is returned.
88      *
89      * @param className the name of the class
90      * @return the JavaClass object
91      * @throws ClassNotFoundException if the class is not in the Repository
92      */
93     @Override
loadClass(String className)94     public JavaClass loadClass(String className) throws ClassNotFoundException {
95         if ((className == null) || className.isEmpty()) {
96             throw new IllegalArgumentException("Invalid class name " + className);
97     }
98         className = className.replace('/', '.'); // Just in case, canonical form
99         final JavaClass clazz = findClass(className);
100         if (clazz != null) {
101             return clazz;
102         }
103 
104         IOException e = new IOException("Couldn't find: " + className + ".class");
105         throw new ClassNotFoundException("Exception while looking for class " +
106                 className + ": " + e, e);
107     }
108 
109     /**
110      * Find the JavaClass object for a runtime Class object. If a class with the
111      * same name is already in this Repository, the Repository version is
112      * returned. Otherwise, getResourceAsStream() is called on the Class object
113      * to find the class's representation. If the representation is found, it is
114      * added to the Repository.
115      *
116      * @see Class
117      * @param clazz the runtime Class object
118      * @return JavaClass object for given runtime class
119      * @throws ClassNotFoundException if the class is not in the Repository, and
120      * its representation could not be found
121      */
122     @Override
loadClass(final Class<?> clazz)123     public JavaClass loadClass(final Class<?> clazz) throws ClassNotFoundException {
124         final String className = clazz.getName();
125         final JavaClass repositoryClass = findClass(className);
126         if (repositoryClass != null) {
127             return repositoryClass;
128     }
129         String name = className;
130         final int i = name.lastIndexOf('.');
131         if (i > 0) {
132             name = name.substring(i + 1);
133         }
134         JavaClass cls = null;
135         try (InputStream clsStream = clazz.getResourceAsStream(name + ".class")) {
136             return cls = loadClass(clsStream, className);
137         } catch (final IOException e) {
138             return cls;
139         }
140 
141     }
142 
143 
loadClass(final InputStream is, final String className)144     private JavaClass loadClass(final InputStream is, final String className)
145             throws ClassNotFoundException {
146         try {
147             if (is != null) {
148                 final ClassParser parser = new ClassParser(is, className);
149                 final JavaClass clazz = parser.parse();
150                 storeClass(clazz);
151                 return clazz;
152             }
153         } catch (final IOException e) {
154             throw new ClassNotFoundException("Exception while looking for class "
155                     + className + ": " + e, e);
156         } finally {
157             if (is != null) {
158                 try {
159                     is.close();
160                 } catch (final IOException e) {
161                     // ignored
162                 }
163             }
164         }
165         throw new ClassNotFoundException("SyntheticRepository could not load "
166                 + className);
167     }
168 
169     /**
170      * Clear all entries from cache.
171      */
172     @Override
clear()173     public void clear() {
174         loadedClasses.clear();
175     }
176 }
177