/*
|
* Copyright 2013 gitblit.com.
|
*
|
* 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.gitblit;
|
|
import java.text.MessageFormat;
|
import java.util.Collection;
|
import java.util.Collections;
|
import java.util.HashSet;
|
import java.util.List;
|
import java.util.Set;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import org.eclipse.jgit.api.Git;
|
import org.eclipse.jgit.lib.RefUpdate.Result;
|
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.StoredConfig;
|
import org.eclipse.jgit.transport.FetchResult;
|
import org.eclipse.jgit.transport.RemoteConfig;
|
import org.eclipse.jgit.transport.TrackingRefUpdate;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
|
import com.gitblit.manager.IRepositoryManager;
|
import com.gitblit.models.RepositoryModel;
|
import com.gitblit.models.UserModel;
|
import com.gitblit.utils.JGitUtils;
|
|
/**
|
* The Mirror executor handles periodic fetching of mirrored repositories.
|
*
|
* @author James Moger
|
*
|
*/
|
public class MirrorExecutor implements Runnable {
|
|
private final Logger logger = LoggerFactory.getLogger(MirrorExecutor.class);
|
|
private final Set<String> repairAttempted = Collections.synchronizedSet(new HashSet<String>());
|
|
private final IStoredSettings settings;
|
|
private AtomicBoolean running = new AtomicBoolean(false);
|
|
private AtomicBoolean forceClose = new AtomicBoolean(false);
|
|
private final UserModel gitblitUser;
|
|
public MirrorExecutor(IStoredSettings settings) {
|
this.settings = settings;
|
this.gitblitUser = new UserModel("gitblit");
|
this.gitblitUser.displayName = "Gitblit";
|
}
|
|
public boolean isReady() {
|
return settings.getBoolean(Keys.git.enableMirroring, false);
|
}
|
|
public boolean isRunning() {
|
return running.get();
|
}
|
|
public void close() {
|
forceClose.set(true);
|
}
|
|
@Override
|
public void run() {
|
if (!isReady()) {
|
return;
|
}
|
|
running.set(true);
|
|
IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
|
|
for (String repositoryName : repositoryManager.getRepositoryList()) {
|
if (forceClose.get()) {
|
break;
|
}
|
if (repositoryManager.isCollectingGarbage(repositoryName)) {
|
logger.debug("mirror is skipping {} garbagecollection", repositoryName);
|
continue;
|
}
|
RepositoryModel model = null;
|
Repository repository = null;
|
try {
|
model = repositoryManager.getRepositoryModel(repositoryName);
|
if (!model.isMirror && !model.isBare) {
|
// repository must be a valid bare git mirror
|
logger.debug("mirror is skipping {} !mirror !bare", repositoryName);
|
continue;
|
}
|
|
repository = repositoryManager.getRepository(repositoryName);
|
if (repository == null) {
|
logger.warn(MessageFormat.format("MirrorExecutor is missing repository {0}?!?", repositoryName));
|
continue;
|
}
|
|
// automatically repair (some) invalid fetch ref specs
|
if (!repairAttempted.contains(repositoryName)) {
|
repairAttempted.add(repositoryName);
|
JGitUtils.repairFetchSpecs(repository);
|
}
|
|
// find the first mirror remote - there should only be one
|
StoredConfig rc = repository.getConfig();
|
RemoteConfig mirror = null;
|
List<RemoteConfig> configs = RemoteConfig.getAllRemoteConfigs(rc);
|
for (RemoteConfig config : configs) {
|
if (config.isMirror()) {
|
mirror = config;
|
break;
|
}
|
}
|
|
if (mirror == null) {
|
// repository does not have a mirror remote
|
logger.debug("mirror is skipping {} no mirror remote found", repositoryName);
|
continue;
|
}
|
|
logger.debug("checking {} remote {} for ref updates", repositoryName, mirror.getName());
|
final boolean testing = false;
|
Git git = new Git(repository);
|
FetchResult result = git.fetch().setRemote(mirror.getName()).setDryRun(testing).call();
|
Collection<TrackingRefUpdate> refUpdates = result.getTrackingRefUpdates();
|
if (refUpdates.size() > 0) {
|
for (TrackingRefUpdate ru : refUpdates) {
|
StringBuilder sb = new StringBuilder();
|
sb.append("updated mirror ");
|
sb.append(repositoryName);
|
sb.append(" ");
|
sb.append(ru.getRemoteName());
|
sb.append(" -> ");
|
sb.append(ru.getLocalName());
|
if (ru.getResult() == Result.FORCED) {
|
sb.append(" (forced)");
|
}
|
sb.append(" ");
|
sb.append(ru.getOldObjectId() == null ? "" : ru.getOldObjectId().abbreviate(7).name());
|
sb.append("..");
|
sb.append(ru.getNewObjectId() == null ? "" : ru.getNewObjectId().abbreviate(7).name());
|
logger.info(sb.toString());
|
}
|
}
|
} catch (Exception e) {
|
logger.error("Error updating mirror " + repositoryName, e);
|
} finally {
|
// cleanup
|
if (repository != null) {
|
repository.close();
|
}
|
}
|
}
|
|
running.set(false);
|
}
|
}
|