/*
* Copyright 2011 the original author or authors.
*
* Licensed 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
*
* http://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.gradle.api.internal.artifacts.ivyservice.resolutionstrategy;
import org.gradle.api.Action;
import org.gradle.api.artifacts.ArtifactIdentifier;
import org.gradle.api.artifacts.ModuleIdentifier;
import org.gradle.api.artifacts.ModuleVersionIdentifier;
import org.gradle.api.artifacts.ResolvedModuleVersion;
import org.gradle.api.artifacts.cache.ArtifactResolutionControl;
import org.gradle.api.artifacts.cache.DependencyResolutionControl;
import org.gradle.api.artifacts.cache.ModuleResolutionControl;
import org.gradle.api.artifacts.cache.ResolutionControl;
import org.gradle.api.artifacts.cache.ResolutionRules;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory;
import org.gradle.api.internal.artifacts.configurations.MutationValidator;
import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy;
import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static org.gradle.api.internal.artifacts.configurations.MutationValidator.MutationType.STRATEGY;
public class DefaultCachePolicy implements CachePolicy, ResolutionRules {
private static final int SECONDS_IN_DAY = 24 * 60 * 60;
final List<Action<? super DependencyResolutionControl>> dependencyCacheRules;
final List<Action<? super ModuleResolutionControl>> moduleCacheRules;
final List<Action<? super ArtifactResolutionControl>> artifactCacheRules;
private final ImmutableModuleIdentifierFactory moduleIdentifierFactory;
private MutationValidator mutationValidator = MutationValidator.IGNORE;
public DefaultCachePolicy(ImmutableModuleIdentifierFactory moduleIdentifierFactory) {
this.moduleIdentifierFactory = moduleIdentifierFactory;
this.dependencyCacheRules = new ArrayList<Action<? super DependencyResolutionControl>>();
this.moduleCacheRules = new ArrayList<Action<? super ModuleResolutionControl>>();
this.artifactCacheRules = new ArrayList<Action<? super ArtifactResolutionControl>>();
cacheDynamicVersionsFor(SECONDS_IN_DAY, TimeUnit.SECONDS);
cacheChangingModulesFor(SECONDS_IN_DAY, TimeUnit.SECONDS);
cacheMissingArtifactsFor(SECONDS_IN_DAY, TimeUnit.SECONDS);
}
DefaultCachePolicy(DefaultCachePolicy policy) {
this.moduleIdentifierFactory = policy.moduleIdentifierFactory;
this.dependencyCacheRules = new ArrayList<Action<? super DependencyResolutionControl>>(policy.dependencyCacheRules);
this.moduleCacheRules = new ArrayList<Action<? super ModuleResolutionControl>>(policy.moduleCacheRules);
this.artifactCacheRules = new ArrayList<Action<? super ArtifactResolutionControl>>(policy.artifactCacheRules);
}
/**
* Sets the validator to invoke prior to each mutation.
*/
public void setMutationValidator(MutationValidator validator) {
this.mutationValidator = validator;
}
public void eachDependency(Action<? super DependencyResolutionControl> rule) {
mutationValidator.validateMutation(STRATEGY);
dependencyCacheRules.add(0, rule);
}
public void eachModule(Action<? super ModuleResolutionControl> rule) {
mutationValidator.validateMutation(STRATEGY);
moduleCacheRules.add(0, rule);
}
public void eachArtifact(Action<? super ArtifactResolutionControl> rule) {
mutationValidator.validateMutation(STRATEGY);
artifactCacheRules.add(0, rule);
}
public void cacheDynamicVersionsFor(final int value, final TimeUnit unit) {
eachDependency(new Action<DependencyResolutionControl>() {
public void execute(DependencyResolutionControl dependencyResolutionControl) {
dependencyResolutionControl.cacheFor(value, unit);
}
});
}
public void cacheChangingModulesFor(final int value, final TimeUnit units) {
eachModule(new Action<ModuleResolutionControl>() {
public void execute(ModuleResolutionControl moduleResolutionControl) {
if (moduleResolutionControl.isChanging()) {
moduleResolutionControl.cacheFor(value, units);
}
}
});
eachArtifact(new Action<ArtifactResolutionControl>() {
public void execute(ArtifactResolutionControl artifactResolutionControl) {
if (artifactResolutionControl.belongsToChangingModule()) {
artifactResolutionControl.cacheFor(value, units);
}
}
});
}
private void cacheMissingArtifactsFor(final int value, final TimeUnit units) {
eachArtifact(new Action<ArtifactResolutionControl>() {
public void execute(ArtifactResolutionControl artifactResolutionControl) {
if (artifactResolutionControl.getCachedResult() == null) {
artifactResolutionControl.cacheFor(value, units);
}
}
});
}
public boolean mustRefreshVersionList(final ModuleIdentifier moduleIdentifier, Set<ModuleVersionIdentifier> matchingVersions, long ageMillis) {
CachedDependencyResolutionControl dependencyResolutionControl = new CachedDependencyResolutionControl(moduleIdentifier, matchingVersions, ageMillis);
for (Action<? super DependencyResolutionControl> rule : dependencyCacheRules) {
rule.execute(dependencyResolutionControl);
if (dependencyResolutionControl.ruleMatch()) {
return dependencyResolutionControl.mustCheck();
}
}
return false;
}
public boolean mustRefreshMissingModule(ModuleComponentIdentifier component, long ageMillis) {
return mustRefreshModule(component, null, ageMillis, false);
}
public boolean mustRefreshModule(ModuleComponentIdentifier component, ResolvedModuleVersion resolvedModuleVersion, long ageMillis) {
return mustRefreshModule(component, resolvedModuleVersion, ageMillis, false);
}
public boolean mustRefreshChangingModule(ModuleComponentIdentifier component, ResolvedModuleVersion resolvedModuleVersion, long ageMillis) {
return mustRefreshModule(component, resolvedModuleVersion, ageMillis, true);
}
private boolean mustRefreshModule(ModuleComponentIdentifier component, ResolvedModuleVersion version, long ageMillis, boolean changingModule) {
return mustRefreshModule(moduleIdentifierFactory.moduleWithVersion(component.getGroup(), component.getModule(), component.getVersion()), version, ageMillis, changingModule);
}
private boolean mustRefreshModule(ModuleVersionIdentifier moduleVersionId, ResolvedModuleVersion version, long ageMillis, boolean changingModule) {
CachedModuleResolutionControl moduleResolutionControl = new CachedModuleResolutionControl(moduleVersionId, version, changingModule, ageMillis);
for (Action<? super ModuleResolutionControl> rule : moduleCacheRules) {
rule.execute(moduleResolutionControl);
if (moduleResolutionControl.ruleMatch()) {
return moduleResolutionControl.mustCheck();
}
}
return false;
}
public boolean mustRefreshModuleArtifacts(ModuleVersionIdentifier moduleVersionId, Set<ArtifactIdentifier> artifacts,
long ageMillis, boolean belongsToChangingModule, boolean moduleDescriptorInSync) {
if (belongsToChangingModule && !moduleDescriptorInSync) {
return true;
}
return mustRefreshModule(moduleVersionId, new DefaultResolvedModuleVersion(moduleVersionId), ageMillis, belongsToChangingModule);
}
public boolean mustRefreshArtifact(ArtifactIdentifier artifactIdentifier, File cachedArtifactFile, long ageMillis, boolean belongsToChangingModule, boolean moduleDescriptorInSync) {
CachedArtifactResolutionControl artifactResolutionControl = new CachedArtifactResolutionControl(artifactIdentifier, cachedArtifactFile, ageMillis, belongsToChangingModule);
if(belongsToChangingModule && !moduleDescriptorInSync){
return true;
}
for (Action<? super ArtifactResolutionControl> rule : artifactCacheRules) {
rule.execute(artifactResolutionControl);
if (artifactResolutionControl.ruleMatch()) {
return artifactResolutionControl.mustCheck();
}
}
return false;
}
DefaultCachePolicy copy() {
return new DefaultCachePolicy(this);
}
private abstract static class AbstractResolutionControl<A, B> implements ResolutionControl<A, B> {
private final A request;
private final B cachedResult;
private final long ageMillis;
private boolean ruleMatch;
private boolean mustCheck;
private AbstractResolutionControl(A request, B cachedResult, long ageMillis) {
this.request = request;
this.cachedResult = cachedResult;
this.ageMillis = correctForClockShift(ageMillis);
}
/**
* If the age < 0, then it's probable that we've had a clock shift. In this case, treat the age as 1ms.
*/
private long correctForClockShift(long ageMillis) {
if (ageMillis < 0) {
return 1;
}
return ageMillis;
}
public A getRequest() {
return request;
}
public B getCachedResult() {
return cachedResult;
}
public void cacheFor(int value, TimeUnit units) {
long expiryMillis = TimeUnit.MILLISECONDS.convert(value, units);
if (ageMillis > expiryMillis) {
setMustCheck(true);
} else {
setMustCheck(false);
}
}
public void useCachedResult() {
setMustCheck(false);
}
public void refresh() {
setMustCheck(true);
}
private void setMustCheck(boolean val) {
ruleMatch = true;
mustCheck = val;
}
public boolean ruleMatch() {
return ruleMatch;
}
public boolean mustCheck() {
return mustCheck;
}
}
private class CachedDependencyResolutionControl extends AbstractResolutionControl<ModuleIdentifier, Set<ModuleVersionIdentifier>> implements DependencyResolutionControl {
private CachedDependencyResolutionControl(ModuleIdentifier request, Set<ModuleVersionIdentifier> result, long ageMillis) {
super(request, result, ageMillis);
}
}
private class CachedModuleResolutionControl extends AbstractResolutionControl<ModuleVersionIdentifier, ResolvedModuleVersion> implements ModuleResolutionControl {
private final boolean changing;
private CachedModuleResolutionControl(ModuleVersionIdentifier moduleVersionId, ResolvedModuleVersion cachedVersion, boolean changing, long ageMillis) {
super(moduleVersionId, cachedVersion, ageMillis);
this.changing = changing;
}
public boolean isChanging() {
return changing;
}
}
private class CachedArtifactResolutionControl extends AbstractResolutionControl<ArtifactIdentifier, File> implements ArtifactResolutionControl {
private final boolean belongsToChangingModule;
private CachedArtifactResolutionControl(ArtifactIdentifier artifactIdentifier, File cachedResult, long ageMillis, boolean belongsToChangingModule) {
super(artifactIdentifier, cachedResult, ageMillis);
this.belongsToChangingModule = belongsToChangingModule;
}
public boolean belongsToChangingModule() {
return belongsToChangingModule;
}
}
}