    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 *   https://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019package org.apache.bcel.util;
    020
    021import java.util.LinkedHashMap;
    022import java.util.Map;
    023
    024import org.apache.bcel.classfile.JavaClass;
    025
    026/**
    027 * Maintains a least-recently-used (LRU) cache of {@link JavaClass} with maximum size {@code cacheSize}.
    028 *
    029 * <p>
    030 * This repository supports a class path consisting of too many JAR files to handle in {@link ClassPathRepository} or
    031 * {@link MemorySensitiveClassPathRepository} without causing {@code OutOfMemoryError}.
    032 * </p>
    033 *
    034 * @since 6.4.0
    035 */
    036public class LruCacheClassPathRepository extends AbstractClassPathRepository {
    037
    038    private final LinkedHashMap<String, JavaClass> loadedClasses;
    039
    040    public LruCacheClassPathRepository(final ClassPath path, final int cacheSize) {
    041        super(path);
    042
    043        if (cacheSize < 1) {
    044            throw new IllegalArgumentException("cacheSize must be a positive number.");
    045        }
    046        final int initialCapacity = (int) (0.75 * cacheSize);
    047        final boolean accessOrder = true; // Evicts least-recently-accessed
    048        loadedClasses = new LinkedHashMap<String, JavaClass>(initialCapacity, cacheSize, accessOrder) {
    049
    050            private static final long serialVersionUID = 1L;
    051
    052            @Override
    053            protected boolean removeEldestEntry(final Map.Entry<String, JavaClass> eldest) {
    054                return size() > cacheSize;
    055            }
    056        };
    057    }
    058
    059    @Override
    060    public void clear() {
    061        loadedClasses.clear();
    062    }
    063
    064    @Override
    065    public JavaClass findClass(final String className) {
    066        return loadedClasses.get(className);
    067    }
    068
    069    @Override
    070    public void removeClass(final JavaClass javaClass) {
    071        loadedClasses.remove(javaClass.getClassName());
    072    }
    073
    074    @Override
    075    public void storeClass(final JavaClass javaClass) {
    076        // Not storing parent's _loadedClass
    077        loadedClasses.put(javaClass.getClassName(), javaClass);
    078        javaClass.setRepository(this);
    079    }
    080}