From e41e8f8c3bc9f5edab1d271464364f95620ece8c Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Thu, 19 Nov 2015 17:55:38 -0500 Subject: [PATCH] Create filestore directory on startup --- src/main/java/com/gitblit/tickets/BranchTicketService.java | 208 +++++++++++++++++++++++++++++++++++++++++----------- 1 files changed, 164 insertions(+), 44 deletions(-) diff --git a/src/main/java/com/gitblit/tickets/BranchTicketService.java b/src/main/java/com/gitblit/tickets/BranchTicketService.java index 60dca27..8396693 100644 --- a/src/main/java/com/gitblit/tickets/BranchTicketService.java +++ b/src/main/java/com/gitblit/tickets/BranchTicketService.java @@ -27,9 +27,8 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; - -import javax.inject.Inject; import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; import org.eclipse.jgit.api.errors.JGitInternalException; @@ -44,21 +43,27 @@ import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefRename; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.RefUpdate.Result; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.ReceiveCommand; import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.eclipse.jgit.treewalk.TreeWalk; import com.gitblit.Constants; +import com.gitblit.git.ReceiveCommandEvent; import com.gitblit.manager.INotificationManager; +import com.gitblit.manager.IPluginManager; import com.gitblit.manager.IRepositoryManager; import com.gitblit.manager.IRuntimeManager; import com.gitblit.manager.IUserManager; import com.gitblit.models.PathModel; +import com.gitblit.models.PathModel.PathChangeModel; import com.gitblit.models.RefModel; import com.gitblit.models.RepositoryModel; import com.gitblit.models.TicketModel; @@ -67,6 +72,8 @@ import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.StringUtils; +import com.google.inject.Inject; +import com.google.inject.Singleton; /** * Implementation of a ticket service based on an orphan branch. All tickets @@ -76,21 +83,10 @@ * @author James Moger * */ +@Singleton public class BranchTicketService extends ITicketService implements RefsChangedListener { - /** - * The event fired by other classes to allow this service to index tickets. - */ - public static class TicketsBranchUpdated extends RefsChangedEvent { - - public final RepositoryModel model; - - public TicketsBranchUpdated(RepositoryModel model) { - this.model = model; - } - } - - public static final String BRANCH = "refs/gitblit/tickets"; + public static final String BRANCH = "refs/meta/gitblit/tickets"; private static final String JOURNAL = "journal.json"; @@ -101,23 +97,26 @@ @Inject public BranchTicketService( IRuntimeManager runtimeManager, + IPluginManager pluginManager, INotificationManager notificationManager, IUserManager userManager, IRepositoryManager repositoryManager) { super(runtimeManager, + pluginManager, notificationManager, userManager, repositoryManager); lastAssignedId = new ConcurrentHashMap<String, AtomicLong>(); - + // register the branch ticket service for repository ref changes Repository.getGlobalListenerList().addRefsChangedListener(this); } @Override public BranchTicketService start() { + log.info("{} started", getClass().getSimpleName()); return this; } @@ -138,39 +137,105 @@ } /** - * Listen for refs changed events and reindex that repository. + * Listen for tickets branch changes and (re)index tickets, as appropriate */ @Override - public void onRefsChanged(RefsChangedEvent event) { - if (!(event instanceof TicketsBranchUpdated)) { + public synchronized void onRefsChanged(RefsChangedEvent event) { + if (!(event instanceof ReceiveCommandEvent)) { return; } - RepositoryModel repository = ((TicketsBranchUpdated) event).model; + + ReceiveCommandEvent branchUpdate = (ReceiveCommandEvent) event; + RepositoryModel repository = branchUpdate.model; + ReceiveCommand cmd = branchUpdate.cmd; try { - reindex(repository); + switch (cmd.getType()) { + case CREATE: + case UPDATE_NONFASTFORWARD: + // reindex everything + reindex(repository); + break; + case UPDATE: + // incrementally index ticket updates + resetCaches(repository); + long start = System.nanoTime(); + log.info("incrementally indexing {} ticket branch due to received ref update", repository.name); + Repository db = repositoryManager.getRepository(repository.name); + try { + Set<Long> ids = new HashSet<Long>(); + List<PathChangeModel> paths = JGitUtils.getFilesInRange(db, + cmd.getOldId().getName(), cmd.getNewId().getName()); + for (PathChangeModel path : paths) { + String name = path.name.substring(path.name.lastIndexOf('/') + 1); + if (!JOURNAL.equals(name)) { + continue; + } + String tid = path.path.split("/")[2]; + long ticketId = Long.parseLong(tid); + if (!ids.contains(ticketId)) { + ids.add(ticketId); + TicketModel ticket = getTicket(repository, ticketId); + log.info(MessageFormat.format("indexing ticket #{0,number,0}: {1}", + ticketId, ticket.title)); + indexer.index(ticket); + } + } + long end = System.nanoTime(); + log.info("incremental indexing of {0} ticket(s) completed in {1} msecs", + ids.size(), TimeUnit.NANOSECONDS.toMillis(end - start)); + } finally { + db.close(); + } + break; + default: + log.warn("Unexpected receive type {} in BranchTicketService.onRefsChanged" + cmd.getType()); + break; + } } catch (Exception e) { log.error("failed to reindex " + repository.name, e); } } /** - * Returns a RefModel for the refs/gitblit/tickets branch in the repository. + * Returns a RefModel for the refs/meta/gitblit/tickets branch in the repository. * If the branch can not be found, null is returned. * * @return a refmodel for the gitblit tickets branch or null */ private RefModel getTicketsBranch(Repository db) { - List<RefModel> refs = JGitUtils.getRefs(db, Constants.R_GITBLIT); + List<RefModel> refs = JGitUtils.getRefs(db, "refs/"); + Ref oldRef = null; for (RefModel ref : refs) { if (ref.reference.getName().equals(BRANCH)) { return ref; + } else if (ref.reference.getName().equals("refs/gitblit/tickets")) { + oldRef = ref.reference; + } + } + if (oldRef != null) { + // rename old ref to refs/meta/gitblit/tickets + RefRename cmd; + try { + cmd = db.renameRef(oldRef.getName(), BRANCH); + cmd.setRefLogIdent(new PersonIdent("Gitblit", "gitblit@localhost")); + cmd.setRefLogMessage("renamed " + oldRef.getName() + " => " + BRANCH); + Result res = cmd.rename(); + switch (res) { + case RENAMED: + log.info(db.getDirectory() + " " + cmd.getRefLogMessage()); + return getTicketsBranch(db); + default: + log.error("failed to rename " + oldRef.getName() + " => " + BRANCH + " (" + res.name() + ")"); + } + } catch (IOException e) { + log.error("failed to rename tickets branch", e); } } return null; } /** - * Creates the refs/gitblit/tickets branch. + * Creates the refs/meta/gitblit/tickets branch. * @param db */ private void createTicketsBranch(Repository db) { @@ -183,7 +248,7 @@ * folder with the remaining characters as a subfolder within that folder. * * @param ticketId - * @return the root path of the ticket content on the refs/gitblit/tickets branch + * @return the root path of the ticket content on the refs/meta/gitblit/tickets branch */ private String toTicketPath(long ticketId) { StringBuilder sb = new StringBuilder(); @@ -232,7 +297,7 @@ log.error("failed to read " + file, e); } finally { if (rw != null) { - rw.release(); + rw.close(); } } return null; @@ -288,7 +353,7 @@ } catch (IOException e) { log.error("", e); } finally { - inserter.release(); + inserter.close(); } } @@ -318,6 +383,37 @@ } /** + * Returns the assigned ticket ids. + * + * @return the assigned ticket ids + */ + @Override + public synchronized Set<Long> getIds(RepositoryModel repository) { + Repository db = repositoryManager.getRepository(repository.name); + try { + if (getTicketsBranch(db) == null) { + return Collections.emptySet(); + } + Set<Long> ids = new TreeSet<Long>(); + List<PathModel> paths = JGitUtils.getDocuments(db, Arrays.asList("json"), BRANCH); + for (PathModel path : paths) { + String name = path.name.substring(path.name.lastIndexOf('/') + 1); + if (!JOURNAL.equals(name)) { + continue; + } + String tid = path.path.split("/")[2]; + long ticketId = Long.parseLong(tid); + ids.add(ticketId); + } + return ids; + } finally { + if (db != null) { + db.close(); + } + } + } + + /** * Assigns a new ticket id. * * @param repository @@ -338,16 +434,10 @@ } AtomicLong lastId = lastAssignedId.get(repository.name); if (lastId.get() <= 0) { - List<PathModel> paths = JGitUtils.getDocuments(db, Arrays.asList("json"), BRANCH); - for (PathModel path : paths) { - String name = path.name.substring(path.name.lastIndexOf('/') + 1); - if (!JOURNAL.equals(name)) { - continue; - } - String tid = path.path.split("/")[2]; - long ticketId = Long.parseLong(tid); - if (ticketId > lastId.get()) { - lastId.set(ticketId); + Set<Long> ids = getIds(repository); + for (long id : ids) { + if (id > lastId.get()) { + lastId.set(id); } } } @@ -460,6 +550,28 @@ ticket.number = ticketId; } return ticket; + } finally { + db.close(); + } + } + + /** + * Retrieves the journal for the ticket. + * + * @param repository + * @param ticketId + * @return a journal, if it exists, otherwise null + */ + @Override + protected List<Change> getJournalImpl(RepositoryModel repository, long ticketId) { + Repository db = repositoryManager.getRepository(repository.name); + try { + List<Change> changes = getJournal(db, ticketId); + if (ArrayUtils.isEmpty(changes)) { + log.warn("Empty journal for {}:{}", repository, ticketId); + return null; + } + return changes; } finally { db.close(); } @@ -599,7 +711,9 @@ ticket.number, db.getDirectory()), t); } finally { // release the treewalk - treeWalk.release(); + if (treeWalk != null) { + treeWalk.close(); + } } } finally { db.close(); @@ -697,7 +811,7 @@ // finish the index builder.finish(); } finally { - inserter.release(); + inserter.close(); } return newIndex; } @@ -714,8 +828,12 @@ List<DirCacheEntry> list = new ArrayList<DirCacheEntry>(); TreeWalk tw = null; try { - tw = new TreeWalk(db); ObjectId treeId = db.resolve(BRANCH + "^{tree}"); + if (treeId == null) { + // branch does not exist yet, could be migrating tickets + return list; + } + tw = new TreeWalk(db); int hIdx = tw.addTree(treeId); tw.setRecursive(true); @@ -737,7 +855,7 @@ } } finally { if (tw != null) { - tw.release(); + tw.close(); } } return list; @@ -795,10 +913,10 @@ rc)); } } finally { - revWalk.release(); + revWalk.close(); } } finally { - odi.release(); + odi.close(); } return success; } @@ -815,7 +933,9 @@ } catch (Exception e) { log.error(null, e); } finally { - db.close(); + if (db != null) { + db.close(); + } } return false; } -- Gitblit v1.9.1