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 49 private SyntheticRepository() { 50 } 51 52 public static SyntheticRepository getInstance() { 53 return new SyntheticRepository(); 54 } 55 56 /** 57 * Store a new JavaClass instance into this Repository. 58 */ 59 @Override 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 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 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 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 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 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 173 public void clear() { 174 loadedClasses.clear(); 175 } 176 }