/* Copyright (c) 2013-2014 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: * David Winslow (Boundless) - initial implementation */ package org.locationtech.geogig.storage.mongo; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import javax.annotation.Nullable; import org.locationtech.geogig.api.ObjectId; import org.locationtech.geogig.api.plumbing.merge.Conflict; import org.locationtech.geogig.repository.RepositoryConnectionException; import org.locationtech.geogig.storage.AbstractStagingDatabase; import org.locationtech.geogig.storage.ConfigDatabase; import org.locationtech.geogig.storage.ObjectDatabase; import org.locationtech.geogig.storage.StagingDatabase; import com.google.common.base.Optional; import com.google.common.base.Suppliers; import com.google.inject.Inject; import com.mongodb.BasicDBObject; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; /** * A staging database that uses a MongoDB server for persistence. */ public class MongoStagingDatabase extends AbstractStagingDatabase implements StagingDatabase { protected DBCollection conflicts; private ConfigDatabase config; @Inject public MongoStagingDatabase(final ConfigDatabase config, final MongoConnectionManager manager, final ObjectDatabase repositoryDb, ExecutorService executor) { super(Suppliers.ofInstance(repositoryDb), Suppliers.ofInstance(new MongoObjectDatabase( config, manager, "staging", executor))); this.config = config; } @Override synchronized public void open() { super.open(); conflicts = ((MongoObjectDatabase) super.stagingDb).getCollection("conflicts"); conflicts.ensureIndex("path"); } @Override synchronized public void close() { super.close(); conflicts = null; } @Override public Optional<Conflict> getConflict(@Nullable String namespace, String path) { DBObject query = new BasicDBObject(); query.put("path", path); if (namespace != null) { query.put("namespace", namespace); } DBObject result = conflicts.findOne(query); if (result == null) { return Optional.absent(); } else { ObjectId ancestor = ObjectId.valueOf((String) result.get("ancestor")); ObjectId ours = ObjectId.valueOf((String) result.get("ours")); ObjectId theirs = ObjectId.valueOf((String) result.get("theirs")); return Optional.of(new Conflict(path, ancestor, ours, theirs)); } } @Override public boolean hasConflicts(String namespace) { DBObject query = new BasicDBObject(); if (namespace == null) { query.put("namespace", 0); } else { query.put("namespace", namespace); } long count = conflicts.count(query); return count > 0; } @Override public List<Conflict> getConflicts(@Nullable String namespace, @Nullable String pathFilter) { DBObject query = new BasicDBObject(); if (namespace == null) { query.put("namespace", 0); } else { query.put("namespace", namespace); } if (pathFilter != null) { DBObject regex = new BasicDBObject(); regex.put("$regex", "^" + pathFilter); query.put("path", regex); } DBCursor cursor = conflicts.find(query); List<Conflict> results = new ArrayList<Conflict>(); while (cursor.hasNext()) { DBObject element = cursor.next(); String path = (String) element.get("path"); ObjectId ancestor = ObjectId.valueOf((String) element.get("ancestor")); ObjectId ours = ObjectId.valueOf((String) element.get("ours")); ObjectId theirs = ObjectId.valueOf((String) element.get("theirs")); results.add(new Conflict(path, ancestor, ours, theirs)); } return results; } @Override public void addConflict(@Nullable String namespace, Conflict conflict) { DBObject query = new BasicDBObject(); query.put("path", conflict.getPath()); if (namespace == null) { query.put("namespace", 0); } else { query.put("namespace", namespace); } DBObject record = new BasicDBObject(); if (namespace == null) { record.put("namespace", 0); } else { record.put("namespace", namespace); } record.put("path", conflict.getPath()); record.put("ancestor", conflict.getAncestor().toString()); record.put("ours", conflict.getOurs().toString()); record.put("theirs", conflict.getTheirs().toString()); conflicts.update(query, record, true, false); } @Override public void removeConflict(@Nullable String namespace, String path) { DBObject query = new BasicDBObject(); if (namespace == null) { query.put("namespace", 0); } else { query.put("namespace", namespace); } query.put("path", path); conflicts.remove(query); } @Override public void removeConflicts(@Nullable String namespace) { DBObject query = new BasicDBObject(); if (namespace == null) { query.put("namespace", 0); } else { query.put("namespace", namespace); } conflicts.remove(query); } @Override public void configure() throws RepositoryConnectionException { RepositoryConnectionException.StorageType.STAGING.configure(config, "mongodb", "0.1"); } @Override public void checkConfig() throws RepositoryConnectionException { RepositoryConnectionException.StorageType.STAGING.verify(config, "mongodb", "0.1"); } }