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