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 }