Java tutorial
/* Copyright (c) 2017 Boundless and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/edl-v10.html * * Contributors: * Gabriel Roldan (Boundless) - initial implementation */ package org.locationtech.geogig.remotes.pack; import static com.google.common.base.Preconditions.checkNotNull; import static org.locationtech.geogig.storage.BulkOpListener.NOOP_LISTENER; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Function; import org.eclipse.jdt.annotation.Nullable; import org.locationtech.geogig.model.ObjectId; import org.locationtech.geogig.model.Ref; import org.locationtech.geogig.model.RevCommit; import org.locationtech.geogig.model.RevObject; import org.locationtech.geogig.model.RevTag; import org.locationtech.geogig.model.RevTree; import org.locationtech.geogig.remotes.RefDiff; import org.locationtech.geogig.remotes.internal.DeduplicationService; import org.locationtech.geogig.remotes.internal.Deduplicator; import org.locationtech.geogig.repository.ProgressListener; import org.locationtech.geogig.repository.Repository; import org.locationtech.geogig.storage.IndexDatabase; import org.locationtech.geogig.storage.ObjectDatabase; import com.google.common.base.Optional; import com.google.common.base.Stopwatch; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.google.common.collect.Maps; class PackImpl implements Pack { private final Repository source; /** * All missing commits, by {@link RefRequest}, with no duplication (i.e. if a commit is part of * two missing ref histories, it'll be present only on the first one, according to the map's * iteration order) */ private final LinkedHashMap<RefRequest, List<RevCommit>> missingCommits; private LinkedHashMap<RefRequest, List<IndexDef>> missingIndexes; private final List<RevTag> missingTags; protected PackImpl(Repository source, List<RevTag> missingTags, LinkedHashMap<RefRequest, List<RevCommit>> missingCommits, LinkedHashMap<RefRequest, List<IndexDef>> missingIndexes) { checkNotNull(source); checkNotNull(missingTags); checkNotNull(missingCommits); checkNotNull(missingIndexes); this.source = source; this.missingTags = missingTags; this.missingCommits = missingCommits; this.missingIndexes = missingIndexes; } public @Override List<RefDiff> applyTo(PackProcessor target, ProgressListener progress) { checkNotNull(target); checkNotNull(progress); progress.started(); List<RefDiff> result = new ArrayList<>(); List<RefRequest> reqs = Lists.newArrayList(missingCommits.keySet()); Deduplicator deduplicator = DeduplicationService.create(); try { for (RefRequest req : reqs) { RefDiff changedRef = applyToPreOrder(target, req, deduplicator, progress); checkNotNull(changedRef); result.add(changedRef); } } finally { deduplicator.release(); } List<RevTag> tags = this.missingTags; target.putAll(tags.iterator(), NOOP_LISTENER); // process indexes if (!missingIndexes.isEmpty()) { reqs = Lists.newArrayList(missingIndexes.keySet()); deduplicator = DeduplicationService.create(); try { for (RefRequest req : reqs) { applyIndex(target, req, deduplicator, progress); } } finally { deduplicator.release(); } } progress.complete(); return result; } private RefDiff applyToPreOrder(PackProcessor target, RefRequest req, Deduplicator deduplicator, ProgressListener progress) { progress.setDescription("Saving missing revision objects changes for " + req.name); ObjectReporter objectReport = new ObjectReporter(progress); // back up current progress indicator final Function<ProgressListener, String> defaultProgressIndicator; defaultProgressIndicator = progress.progressIndicator(); // set our custom progress indicator progress.setProgressIndicator((p) -> objectReport.toString()); final List<RevCommit> commits = missingCommits.get(req); checkNotNull(commits); final ObjectDatabase sourceStore = source.objectDatabase(); List<ObjectId[]> diffRootTreeIds = collectMissingRootTreeIdPairs(commits, sourceStore); final ContentIdsProducer producer = ContentIdsProducer.forCommits(sourceStore, diffRootTreeIds, deduplicator, objectReport); final ExecutorService producerThread = Executors.newSingleThreadExecutor(); try { producerThread.submit(producer); Iterator<ObjectId> missingContentIds = producer.iterator(); Iterator<RevObject> allObjects; { Iterator<RevObject> missingContents; Iterator<RevCommit> commitsIterator; missingContents = sourceStore.getAll(() -> missingContentIds); commitsIterator = Iterators.filter(commits.iterator(), (c) -> { objectReport.addCommit(); return true; }); allObjects = Iterators.concat(missingContents, commitsIterator); } final Stopwatch sw = Stopwatch.createStarted(); target.putAll(allObjects, objectReport); progress.complete(); if (objectReport.total.get() > 0) { progress.started(); String description = String.format("Objects inserted: %,d, repeated: %,d, time: %s", objectReport.inserted(), objectReport.found(), sw.stop()); progress.setDescription(description); } } finally { producerThread.shutdownNow(); // restore previous progress indicator progress.setProgressIndicator(defaultProgressIndicator); } Ref oldRef = req.have.isPresent() ? new Ref(req.name, req.have.get()) : null; Ref newRef = new Ref(req.name, req.want); RefDiff changedRef = new RefDiff(oldRef, newRef); return changedRef; } private List<ObjectId[]> collectMissingRootTreeIdPairs(List<RevCommit> commits, ObjectDatabase sourceStore) { final Map<ObjectId, RevCommit> rootsById = new HashMap<>(Maps.uniqueIndex(commits, (c) -> c.getId())); List<ObjectId[]> diffRootTreeIds = new ArrayList<>(); for (RevCommit commit : commits) { final ObjectId rightTreeId = commit.getTreeId(); List<ObjectId> parentIds = commit.getParentIds(); if (parentIds.isEmpty()) { diffRootTreeIds.add(new ObjectId[] { RevTree.EMPTY_TREE_ID, rightTreeId }); continue; } for (ObjectId parentId : parentIds) { final @Nullable RevCommit parent = parentId.isNull() ? null : Optional.fromNullable((RevCommit) rootsById.get(parentId)) .or(() -> source.getCommit(parentId)); ObjectId oldRootTreeId = parent == null ? RevTree.EMPTY_TREE_ID : parent.getTreeId(); diffRootTreeIds.add(new ObjectId[] { oldRootTreeId, rightTreeId }); } } return diffRootTreeIds; } private void applyIndex(PackProcessor target, RefRequest req, Deduplicator deduplicator, ProgressListener progress) { progress.setDescription("Updating spatial indexes for " + req.name); ObjectReporter objectReport = new ObjectReporter(progress); // back up current progress indicator final Function<ProgressListener, String> defaultProgressIndicator; defaultProgressIndicator = progress.progressIndicator(); // set our custom progress indicator progress.setProgressIndicator((p) -> objectReport.toString()); final List<IndexDef> indexes = missingIndexes.get(req); checkNotNull(indexes); final IndexDatabase sourceStore = source.indexDatabase(); try { final Stopwatch sw = Stopwatch.createStarted(); for (IndexDef def : indexes) { target.putIndex(def, sourceStore, objectReport, deduplicator); } progress.complete(); if (objectReport.total.get() > 0) { progress.started(); String description = String.format("Indexes updated: %,d, repeated: %,d, time: %s", objectReport.inserted(), objectReport.found(), sw.stop()); progress.setDescription(description); } } finally { // restore previous progress indicator progress.setProgressIndicator(defaultProgressIndicator); } } }