| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| package org.apache.ivy.core.cache; |
| |
| import org.apache.ivy.core.module.descriptor.ModuleDescriptor; |
| import org.apache.ivy.plugins.parser.ParserSettings; |
| import org.apache.ivy.util.Message; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.text.ParseException; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| |
| /** |
| * Cache ModuleDescriptors so that when the same module is used twice (in multi-module build for |
| * instance), it is parsed only once. This cache is has a limited size, and keep the most recently |
| * used entries. The entry in the cache are invalidated if there is a change to one variable used in |
| * the module descriptor. |
| */ |
| class ModuleDescriptorMemoryCache { |
| |
| private final int maxSize; |
| |
| private final LinkedHashMap<File, CacheEntry> valueMap; |
| |
| /** |
| * Create a cache of the given size |
| * |
| * @param size int |
| */ |
| public ModuleDescriptorMemoryCache(int size) { |
| this.maxSize = size; |
| this.valueMap = new LinkedHashMap<>(size); |
| } |
| |
| public ModuleDescriptor get(File ivyFile, ParserSettings ivySettings, boolean validated, |
| ModuleDescriptorProvider mdProvider) throws ParseException, IOException { |
| |
| ModuleDescriptor descriptor = getFromCache(ivyFile, ivySettings, validated); |
| if (descriptor == null) { |
| descriptor = getStale(ivyFile, ivySettings, validated, mdProvider); |
| } |
| return descriptor; |
| } |
| |
| /** |
| * Get the module descriptor from the mdProvider and store it into the cache. |
| */ |
| public ModuleDescriptor getStale(File ivyFile, ParserSettings ivySettings, boolean validated, |
| ModuleDescriptorProvider mdProvider) throws ParseException, IOException { |
| ParserSettingsMonitor settingsMonitor = new ParserSettingsMonitor(ivySettings); |
| ModuleDescriptor descriptor = mdProvider.provideModule( |
| settingsMonitor.getMonitoredSettings(), ivyFile, validated); |
| putInCache(ivyFile, settingsMonitor, validated, descriptor); |
| return descriptor; |
| } |
| |
| ModuleDescriptor getFromCache(File ivyFile, ParserSettings ivySettings, boolean validated) { |
| if (maxSize <= 0) { |
| // cache is disabled |
| return null; |
| } |
| synchronized (valueMap) { |
| CacheEntry entry = valueMap.get(ivyFile); |
| if (entry != null) { |
| if (entry.isStale(ivyFile, validated, ivySettings)) { |
| Message.debug("Entry is found in the ModuleDescriptorCache but entry should be " |
| + "reevaluated : " + ivyFile); |
| valueMap.remove(ivyFile); |
| return null; |
| } else { |
| // Move the entry at the end of the list |
| valueMap.remove(ivyFile); |
| valueMap.put(ivyFile, entry); |
| Message.debug("Entry is found in the ModuleDescriptorCache : " + ivyFile); |
| return entry.md; |
| } |
| } else { |
| Message.debug("No entry is found in the ModuleDescriptorCache : " + ivyFile); |
| return null; |
| } |
| } |
| } |
| |
| void putInCache(File url, ParserSettingsMonitor ivySettingsMonitor, boolean validated, |
| ModuleDescriptor descriptor) { |
| if (maxSize <= 0) { |
| // cache is disabled |
| return; |
| } |
| synchronized (valueMap) { |
| if (valueMap.size() >= maxSize) { |
| Message.debug("ModuleDescriptorCache is full, remove one entry"); |
| Iterator<CacheEntry> it = valueMap.values().iterator(); |
| it.next(); |
| it.remove(); |
| } |
| valueMap.put(url, new CacheEntry(descriptor, validated, ivySettingsMonitor)); |
| } |
| } |
| |
| private static class CacheEntry { |
| private final ModuleDescriptor md; |
| |
| private final boolean validated; |
| |
| private final ParserSettingsMonitor parserSettingsMonitor; |
| |
| CacheEntry(ModuleDescriptor md, boolean validated, |
| ParserSettingsMonitor parserSettingsMonitor) { |
| this.md = md; |
| this.validated = validated; |
| this.parserSettingsMonitor = parserSettingsMonitor; |
| } |
| |
| boolean isStale(File ivyFile, boolean validated, ParserSettings newParserSettings) { |
| return (validated && !this.validated) |
| || md.getLastModified() != ivyFile.lastModified() |
| || parserSettingsMonitor.hasChanged(newParserSettings); |
| } |
| } |
| |
| } |