| /* | |
| * 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.service; | |
| 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.ReceiveCommand; | |
| import org.eclipse.jgit.transport.ReceiveCommand.Type; | |
| import org.eclipse.jgit.transport.RemoteConfig; | |
| import org.eclipse.jgit.transport.TrackingRefUpdate; | |
| import org.slf4j.Logger; | |
| import org.slf4j.LoggerFactory; | |
| import com.gitblit.IStoredSettings; | |
| import com.gitblit.Keys; | |
| import com.gitblit.git.ReceiveCommandEvent; | |
| import com.gitblit.manager.IRepositoryManager; | |
| import com.gitblit.models.RepositoryModel; | |
| import com.gitblit.models.UserModel; | |
| import com.gitblit.tickets.BranchTicketService; | |
| import com.gitblit.utils.JGitUtils; | |
| /** | |
| * The Mirror service handles periodic fetching of mirrored repositories. | |
| * | |
| * @author James Moger | |
| * | |
| */ | |
| public class MirrorService implements Runnable { | |
| private final Logger logger = LoggerFactory.getLogger(MirrorService.class); | |
| private final Set<String> repairAttempted = Collections.synchronizedSet(new HashSet<String>()); | |
| private final IStoredSettings settings; | |
| private final IRepositoryManager repositoryManager; | |
| private AtomicBoolean running = new AtomicBoolean(false); | |
| private AtomicBoolean forceClose = new AtomicBoolean(false); | |
| private final UserModel gitblitUser; | |
| public MirrorService( | |
| IStoredSettings settings, | |
| IRepositoryManager repositoryManager) { | |
| this.settings = settings; | |
| this.repositoryManager = repositoryManager; | |
| 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); | |
| 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) { | |
| ReceiveCommand ticketBranchCmd = null; | |
| 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()); | |
| if (BranchTicketService.BRANCH.equals(ru.getLocalName())) { | |
| ReceiveCommand.Type type = null; | |
| switch (ru.getResult()) { | |
| case NEW: | |
| type = Type.CREATE; | |
| break; | |
| case FAST_FORWARD: | |
| type = Type.UPDATE; | |
| break; | |
| case FORCED: | |
| type = Type.UPDATE_NONFASTFORWARD; | |
| break; | |
| default: | |
| type = null; | |
| break; | |
| } | |
| if (type != null) { | |
| ticketBranchCmd = new ReceiveCommand(ru.getOldObjectId(), | |
| ru.getNewObjectId(), ru.getLocalName(), type); | |
| } | |
| } | |
| } | |
| if (ticketBranchCmd != null) { | |
| repository.fireEvent(new ReceiveCommandEvent(model, ticketBranchCmd)); | |
| } | |
| } | |
| } catch (Exception e) { | |
| logger.error("Error updating mirror " + repositoryName, e); | |
| } finally { | |
| // cleanup | |
| if (repository != null) { | |
| repository.close(); | |
| } | |
| } | |
| } | |
| running.set(false); | |
| } | |
| } |