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 }