blob: 62246fa7e7ddb588ecd08f34b46b41a1fc97f9c1 [file] [log] [blame]
/*
* Copyright 2016 The Bazel Authors. All rights reserved.
*
* 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 com.google.idea.common.experiments;
import static com.google.idea.common.experiments.ExperimentsUtil.hashExperimentName;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.SystemProperties;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
/**
* An experiment service that delegates to {@link ExperimentLoader ExperimentLoaders}, in a specific
* order.
*
* <p>It will check system properties first, then an experiment file in the user's home directory,
* then finally all files specified by the system property blaze.experiments.file.
*/
public class ExperimentServiceImpl extends ApplicationComponent.Adapter
implements ExperimentService {
private static final Logger logger = Logger.getInstance(ExperimentServiceImpl.class);
private static final String USER_EXPERIMENT_OVERRIDES_FILE =
SystemProperties.getUserHome() + File.separator + ".intellij-experiments";
private final List<ExperimentLoader> services;
private Map<String, String> experiments;
private int experimentScopeCounter = 0;
public ExperimentServiceImpl(String pluginName) {
this(
new SystemPropertyExperimentLoader(),
new FileExperimentLoader(USER_EXPERIMENT_OVERRIDES_FILE),
new WebExperimentLoader(pluginName));
}
@VisibleForTesting
ExperimentServiceImpl(ExperimentLoader... loaders) {
services = ImmutableList.copyOf(loaders);
}
@Override
public void initComponent() {
if (!ApplicationManager.getApplication().isUnitTestMode()) {
services.forEach(ExperimentLoader::initialize);
}
}
@Override
public boolean getExperiment(String key, boolean defaultValue) {
String property = getExperiment(key);
return property != null ? property.equals("1") : defaultValue;
}
@Override
public String getExperimentString(String key, @Nullable String defaultValue) {
String property = getExperiment(key);
return property != null ? property : defaultValue;
}
@Override
public int getExperimentInt(String key, int defaultValue) {
String property = getExperiment(key);
try {
return property != null ? Integer.parseInt(property) : defaultValue;
} catch (NumberFormatException e) {
logger.warn("Could not parse int for experiment: " + key, e);
return defaultValue;
}
}
@Override
public synchronized void startExperimentScope() {
if (++experimentScopeCounter > 0) {
experiments = getAllExperiments();
}
}
@Override
public synchronized void endExperimentScope() {
if (--experimentScopeCounter <= 0) {
logger.assertTrue(experimentScopeCounter == 0);
experiments = null;
}
}
private Map<String, String> getAllExperiments() {
Map<String, String> experiments = this.experiments;
if (experiments != null) {
return experiments;
} else {
return services
.stream()
.flatMap(service -> service.getExperiments().entrySet().stream())
.collect(
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (first, second) -> first));
}
}
private String getExperiment(String key) {
return getAllExperiments().get(hashExperimentName(key));
}
}