From 3f5b8f5d9203aa7ffb7fbe9cdbaf9dba3da6cae6 Mon Sep 17 00:00:00 2001
From: Hybris95 <hybris_95@hotmail.com>
Date: Thu, 01 May 2014 16:14:15 -0400
Subject: [PATCH] Fixes sort, page building and search functions on "my tickets" page.

---
 src/main/java/com/gitblit/tickets/BranchTicketService.java |  238 ++++++++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 161 insertions(+), 77 deletions(-)

diff --git a/src/main/java/com/gitblit/tickets/BranchTicketService.java b/src/main/java/com/gitblit/tickets/BranchTicketService.java
index 60dca27..7d6926d 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;
@@ -78,19 +83,7 @@
  */
 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";
 
@@ -98,20 +91,21 @@
 
 	private final Map<String, AtomicLong> lastAssignedId;
 
-	@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);
 	}
@@ -138,39 +132,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 +243,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();
@@ -376,64 +436,84 @@
 	@Override
 	public List<TicketModel> getTickets(RepositoryModel repository, TicketFilter filter) {
 		List<TicketModel> list = new ArrayList<TicketModel>();
-
-		Repository db = repositoryManager.getRepository(repository.name);
-		try {
-			RefModel ticketsBranch = getTicketsBranch(db);
-			if (ticketsBranch == null) {
-				return list;
+		List<Repository> databases = new ArrayList<Repository>();
+		List<RepositoryModel> models = new ArrayList<RepositoryModel>();
+		
+		if(repository == null)
+		{
+			List<String> allRepo = repositoryManager.getRepositoryList();
+			for(int i = 0; i < allRepo.size(); i++)
+			{
+				databases.add(repositoryManager.getRepository(allRepo.get(i)));
+				models.add(repositoryManager.getRepositoryModel(allRepo.get(i)));
 			}
+		}
+		else
+		{
+			databases.add(repositoryManager.getRepository(repository.name));
+			models.add(repository);
+		}
 
-			// Collect the set of all json files
-			List<PathModel> paths = JGitUtils.getDocuments(db, Arrays.asList("json"), BRANCH);
-
-			// Deserialize each ticket and optionally filter out unwanted tickets
-			for (PathModel path : paths) {
-				String name = path.name.substring(path.name.lastIndexOf('/') + 1);
-				if (!JOURNAL.equals(name)) {
-					continue;
+		for(int i = 0; i < databases.size(); i++)
+		{
+			Repository db = databases.get(i);
+			try {
+				RefModel ticketsBranch = getTicketsBranch(db);
+				if (ticketsBranch == null) {
+					return list;
 				}
-				String json = readTicketsFile(db, path.path);
-				if (StringUtils.isEmpty(json)) {
-					// journal was touched but no changes were written
-					continue;
-				}
-				try {
-					// Reconstruct ticketId from the path
-					// id/26/326/journal.json
-					String tid = path.path.split("/")[2];
-					long ticketId = Long.parseLong(tid);
-					List<Change> changes = TicketSerializer.deserializeJournal(json);
-					if (ArrayUtils.isEmpty(changes)) {
-						log.warn("Empty journal for {}:{}", repository, path.path);
+	
+				// Collect the set of all json files
+				List<PathModel> paths = JGitUtils.getDocuments(db, Arrays.asList("json"), BRANCH);
+	
+				// Deserialize each ticket and optionally filter out unwanted tickets
+				for (PathModel path : paths) {
+					String name = path.name.substring(path.name.lastIndexOf('/') + 1);
+					if (!JOURNAL.equals(name)) {
 						continue;
 					}
-					TicketModel ticket = TicketModel.buildTicket(changes);
-					ticket.project = repository.projectPath;
-					ticket.repository = repository.name;
-					ticket.number = ticketId;
-
-					// add the ticket, conditionally, to the list
-					if (filter == null) {
-						list.add(ticket);
-					} else {
-						if (filter.accept(ticket)) {
-							list.add(ticket);
-						}
+					String json = readTicketsFile(db, path.path);
+					if (StringUtils.isEmpty(json)) {
+						// journal was touched but no changes were written
+						continue;
 					}
-				} catch (Exception e) {
-					log.error("failed to deserialize {}/{}\n{}",
-							new Object [] { repository, path.path, e.getMessage()});
-					log.error(null, e);
+					try {
+						// Reconstruct ticketId from the path
+						// id/26/326/journal.json
+						String tid = path.path.split("/")[2];
+						long ticketId = Long.parseLong(tid);
+						List<Change> changes = TicketSerializer.deserializeJournal(json);
+						if (ArrayUtils.isEmpty(changes)) {
+							log.warn("Empty journal for {}:{}", models.get(i), path.path);
+							continue;
+						}
+						TicketModel ticket = TicketModel.buildTicket(changes);
+						ticket.project = models.get(i).projectPath;
+						ticket.repository = models.get(i).name;
+						ticket.number = ticketId;
+	
+						// add the ticket, conditionally, to the list
+						if (filter == null) {
+							list.add(ticket);
+						} else {
+							if (filter.accept(ticket)) {
+								list.add(ticket);
+							}
+						}
+					} catch (Exception e) {
+						log.error("failed to deserialize {}/{}\n{}",
+								new Object [] { repository, path.path, e.getMessage()});
+						log.error(null, e);
+					}
 				}
+			} finally {
+				db.close();
 			}
-
-			// sort the tickets by creation
-			Collections.sort(list);
-			return list;
-		} finally {
-			db.close();
 		}
+
+		// sort the tickets by creation
+		Collections.sort(list);
+		return list;
 	}
 
 	/**
@@ -599,7 +679,9 @@
 						ticket.number, db.getDirectory()), t);
 			} finally {
 				// release the treewalk
-				treeWalk.release();
+				if (treeWalk != null) {
+					treeWalk.release();
+				}
 			}
 		} finally {
 			db.close();
@@ -815,7 +897,9 @@
 		} catch (Exception e) {
 			log.error(null, e);
 		} finally {
-			db.close();
+			if (db != null) {
+				db.close();
+			}
 		}
 		return false;
 	}

--
Gitblit v1.9.1