mirror of
				https://github.com/scm-manager/scm-manager.git
				synced 2025-10-31 10:35:56 +01:00 
			
		
		
		
	Post receive hook after import (#1544)
Fire post receive repository hook event after pull from remote and after unbundle (Git, HG and SVN) Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com>
This commit is contained in:
		| @@ -24,8 +24,6 @@ | ||||
|  | ||||
| package sonia.scm.repository.spi; | ||||
|  | ||||
| //~--- non-JDK imports -------------------------------------------------------- | ||||
|  | ||||
| import com.google.common.base.Preconditions; | ||||
| import com.google.common.base.Strings; | ||||
| import com.google.common.collect.Lists; | ||||
| @@ -46,49 +44,34 @@ import java.util.List; | ||||
|  | ||||
| import static sonia.scm.repository.SvnUtil.parseRevision; | ||||
|  | ||||
| //~--- JDK imports ------------------------------------------------------------ | ||||
| public class SvnLogCommand extends AbstractSvnCommand implements LogCommand { | ||||
|  | ||||
| public class SvnLogCommand extends AbstractSvnCommand implements LogCommand | ||||
| { | ||||
|   private static final Logger LOG = LoggerFactory.getLogger(SvnLogCommand.class); | ||||
|  | ||||
|   /** | ||||
|    * the logger for SvnLogCommand | ||||
|    */ | ||||
|   private static final Logger logger = | ||||
|     LoggerFactory.getLogger(SvnLogCommand.class); | ||||
|  | ||||
|   SvnLogCommand(SvnContext context) | ||||
|   { | ||||
|   SvnLogCommand(SvnContext context) { | ||||
|     super(context); | ||||
|   } | ||||
|  | ||||
|   //~--- get methods ---------------------------------------------------------- | ||||
|  | ||||
|   @Override | ||||
|   @SuppressWarnings("unchecked") | ||||
|   public Changeset getChangeset(String revision, LogCommandRequest request) { | ||||
|     Changeset changeset = null; | ||||
|  | ||||
|     if (logger.isDebugEnabled()) | ||||
|     { | ||||
|       logger.debug("fetch changeset {}", revision); | ||||
|     if (LOG.isDebugEnabled()) { | ||||
|       LOG.debug("fetch changeset {}", revision); | ||||
|     } | ||||
|  | ||||
|     try | ||||
|     { | ||||
|     try { | ||||
|       long revisioNumber = parseRevision(revision, repository); | ||||
|       Preconditions.checkArgument(revisioNumber > 0, "revision must be greater than zero: %d", revisioNumber); | ||||
|       SVNRepository repo = open(); | ||||
|       Collection<SVNLogEntry> entries = repo.log(null, null, revisioNumber, | ||||
|                                           revisioNumber, true, true); | ||||
|         revisioNumber, true, true); | ||||
|  | ||||
|       if (Util.isNotEmpty(entries)) | ||||
|       { | ||||
|       if (Util.isNotEmpty(entries)) { | ||||
|         changeset = SvnUtil.createChangeset(entries.iterator().next()); | ||||
|       } | ||||
|     } | ||||
|     catch (SVNException ex) | ||||
|     { | ||||
|     } catch (SVNException ex) { | ||||
|       throw new InternalRepositoryException(repository, "could not open repository", ex); | ||||
|     } | ||||
|  | ||||
| @@ -98,73 +81,48 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand | ||||
|   @Override | ||||
|   @SuppressWarnings("unchecked") | ||||
|   public ChangesetPagingResult getChangesets(LogCommandRequest request) { | ||||
|     if (logger.isDebugEnabled()) | ||||
|     { | ||||
|       logger.debug("fetch changesets for {}", request); | ||||
|     if (LOG.isDebugEnabled()) { | ||||
|       LOG.debug("fetch changesets for {}", request); | ||||
|     } | ||||
|  | ||||
|     ChangesetPagingResult changesets = null; | ||||
|     ChangesetPagingResult changesets; | ||||
|     int start = request.getPagingStart(); | ||||
|     int limit = request.getPagingLimit(); | ||||
|     long startRevision = parseRevision(request.getStartChangeset(), repository); | ||||
|     long endRevision = parseRevision(request.getEndChangeset(), repository); | ||||
|     String[] pathArray = null; | ||||
|  | ||||
|     if (!Strings.isNullOrEmpty(request.getPath())) | ||||
|     { | ||||
|       pathArray = new String[] { request.getPath() }; | ||||
|     if (!Strings.isNullOrEmpty(request.getPath())) { | ||||
|       pathArray = new String[]{request.getPath()}; | ||||
|     } | ||||
|  | ||||
|     try | ||||
|     { | ||||
|     try { | ||||
|       SVNRepository repo = open(); | ||||
|  | ||||
|       if ((startRevision > 0) || (pathArray != null)) | ||||
|       { | ||||
|       if ((startRevision > 0) || (pathArray != null)) { | ||||
|         changesets = getChangesets(repo, startRevision, endRevision, start, | ||||
|           limit, pathArray); | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|       } else { | ||||
|         changesets = getChangesets(repo, start, limit); | ||||
|       } | ||||
|     } | ||||
|     catch (SVNException ex) | ||||
|     { | ||||
|     } catch (SVNException ex) { | ||||
|       throw new InternalRepositoryException(repository, "could not open repository", ex); | ||||
|     } | ||||
|  | ||||
|     return changesets; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   //~--- get methods ---------------------------------------------------------- | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @param repo | ||||
|    * @param start | ||||
|    * @param limit | ||||
|    * | ||||
|    * @return | ||||
|    * | ||||
|    * @throws SVNException | ||||
|    */ | ||||
|   private ChangesetPagingResult getChangesets(SVNRepository repo, int start, | ||||
|     int limit) | ||||
|     throws SVNException | ||||
|   { | ||||
|                                               int limit) | ||||
|     throws SVNException { | ||||
|     long latest = repo.getLatestRevision(); | ||||
|     long startRev = latest - start; | ||||
|     long endRev = Math.max(startRev - (limit - 1), 1); | ||||
|  | ||||
|     final List<Changeset> changesets = Lists.newArrayList(); | ||||
|  | ||||
|     if (startRev > 0) | ||||
|     { | ||||
|       logger.debug("fetch changeset from {} to {}", startRev, endRev); | ||||
|     if (startRev > 0) { | ||||
|       LOG.debug("fetch changeset from {} to {}", startRev, endRev); | ||||
|       repo.log(null, startRev, endRev, true, true, | ||||
|         new ChangesetCollector(changesets)); | ||||
|     } | ||||
| @@ -172,51 +130,30 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand | ||||
|     return new ChangesetPagingResult((int) latest, changesets); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @param repo | ||||
|    * @param startRevision | ||||
|    * @param endRevision | ||||
|    * @param start | ||||
|    * @param limit | ||||
|    * @param path | ||||
|    * | ||||
|    * @return | ||||
|    * | ||||
|    * @throws SVNException | ||||
|    */ | ||||
|   @SuppressWarnings("unchecked") | ||||
|   private ChangesetPagingResult getChangesets(SVNRepository repo, | ||||
|     long startRevision, long endRevision, int start, int limit, String[] path) | ||||
|     throws SVNException | ||||
|   { | ||||
|                                               long startRevision, long endRevision, int start, int limit, String[] path) | ||||
|     throws SVNException { | ||||
|     long startRev; | ||||
|     long endRev = Math.max(endRevision, 0); | ||||
|     long maxRev = repo.getLatestRevision(); | ||||
|  | ||||
|     if (startRevision >= 0l) | ||||
|     { | ||||
|     if (startRevision >= 0) { | ||||
|       startRev = startRevision; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|     } else { | ||||
|       startRev = maxRev; | ||||
|     } | ||||
|  | ||||
|     List<SVNLogEntry> changesetList = Lists.newArrayList(); | ||||
|  | ||||
|     logger.debug("fetch changeset from {} to {} for path {}", startRev, endRev, | ||||
|     LOG.debug("fetch changeset from {} to {} for path {}", startRev, endRev, | ||||
|       path); | ||||
|  | ||||
|     Collection<SVNLogEntry> entries = repo.log(path, null, startRev, endRev, | ||||
|                                         true, true); | ||||
|       true, true); | ||||
|  | ||||
|     for (SVNLogEntry entry : entries) | ||||
|     { | ||||
|       if (entry.getRevision() <= maxRev) | ||||
|       { | ||||
|     for (SVNLogEntry entry : entries) { | ||||
|       if (entry.getRevision() <= maxRev) { | ||||
|         changesetList.add(entry); | ||||
|       } | ||||
|     } | ||||
| @@ -225,17 +162,15 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand | ||||
|     int max = limit + start; | ||||
|     int end = total; | ||||
|  | ||||
|     if ((max > 0) && (end > max)) | ||||
|     { | ||||
|     if ((max > 0) && (end > max)) { | ||||
|       end = max; | ||||
|     } | ||||
|  | ||||
|     if (start < 0) | ||||
|     { | ||||
|     if (start < 0) { | ||||
|       start = 0; | ||||
|     } | ||||
|  | ||||
|     logger.trace( | ||||
|     LOG.trace( | ||||
|       "create sublist from {} to {} of total {} collected changesets", start, | ||||
|       end, total); | ||||
|  | ||||
| @@ -245,45 +180,20 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand | ||||
|       SvnUtil.createChangesets(changesetList)); | ||||
|   } | ||||
|  | ||||
|   //~--- inner classes -------------------------------------------------------- | ||||
|  | ||||
|   /** | ||||
|    * Collect and convert changesets. | ||||
|    * | ||||
|    */ | ||||
|   private static class ChangesetCollector implements ISVNLogEntryHandler | ||||
|   { | ||||
|   private static class ChangesetCollector implements ISVNLogEntryHandler { | ||||
|  | ||||
|     /** | ||||
|      * Constructs ... | ||||
|      * | ||||
|      * | ||||
|      * @param changesets | ||||
|      */ | ||||
|     public ChangesetCollector(Collection<Changeset> changesets) | ||||
|     { | ||||
|     private final Collection<Changeset> changesets; | ||||
|  | ||||
|     public ChangesetCollector(Collection<Changeset> changesets) { | ||||
|       this.changesets = changesets; | ||||
|     } | ||||
|  | ||||
|     //~--- methods ------------------------------------------------------------ | ||||
|  | ||||
|     /** | ||||
|      * Method description | ||||
|      * | ||||
|      * | ||||
|      * @param logEntry | ||||
|      * | ||||
|      * @throws SVNException | ||||
|      */ | ||||
|     @Override | ||||
|     public void handleLogEntry(SVNLogEntry logEntry) throws SVNException | ||||
|     { | ||||
|     public void handleLogEntry(SVNLogEntry logEntry) { | ||||
|       changesets.add(SvnUtil.createChangeset(logEntry)); | ||||
|     } | ||||
|  | ||||
|     //~--- fields ------------------------------------------------------------- | ||||
|  | ||||
|     /** Field description */ | ||||
|     private final Collection<Changeset> changesets; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -26,23 +26,21 @@ package sonia.scm.repository.spi; | ||||
|  | ||||
| import com.google.common.collect.ImmutableSet; | ||||
| import com.google.common.io.Closeables; | ||||
| import sonia.scm.event.ScmEventBus; | ||||
| import sonia.scm.repository.Repository; | ||||
| import sonia.scm.repository.SvnRepositoryHandler; | ||||
| import sonia.scm.repository.SvnWorkingCopyFactory; | ||||
| import sonia.scm.repository.api.Command; | ||||
| import sonia.scm.repository.api.HookContextFactory; | ||||
|  | ||||
| import javax.inject.Inject; | ||||
| import java.io.IOException; | ||||
| import java.util.Set; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author Sebastian Sdorra | ||||
|  */ | ||||
| public class SvnRepositoryServiceProvider extends RepositoryServiceProvider | ||||
| { | ||||
| public class SvnRepositoryServiceProvider extends RepositoryServiceProvider { | ||||
|  | ||||
|   /** Field description */ | ||||
|   //J- | ||||
|   public static final Set<Command> COMMANDS = ImmutableSet.of( | ||||
|     Command.BLAME, Command.BROWSE, Command.CAT, Command.DIFF, | ||||
| @@ -50,101 +48,55 @@ public class SvnRepositoryServiceProvider extends RepositoryServiceProvider | ||||
|   ); | ||||
|   //J+ | ||||
|  | ||||
|   //~--- constructors --------------------------------------------------------- | ||||
|  | ||||
|   @Inject | ||||
|   private final SvnContext context; | ||||
|   private final SvnWorkingCopyFactory workingCopyFactory; | ||||
|   private final HookContextFactory hookContextFactory; | ||||
|   private final ScmEventBus eventBus; | ||||
|  | ||||
|   SvnRepositoryServiceProvider(SvnRepositoryHandler handler, | ||||
|     Repository repository, SvnWorkingCopyFactory workingCopyFactory) | ||||
|   { | ||||
|                                Repository repository, | ||||
|                                SvnWorkingCopyFactory workingCopyFactory, | ||||
|                                HookContextFactory hookContextFactory, | ||||
|                                ScmEventBus eventBus) { | ||||
|     this.context = new SvnContext(repository, handler.getDirectory(repository.getId())); | ||||
|     this.workingCopyFactory = workingCopyFactory; | ||||
|     this.hookContextFactory = hookContextFactory; | ||||
|     this.eventBus = eventBus; | ||||
|   } | ||||
|  | ||||
|   //~--- methods -------------------------------------------------------------- | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @throws IOException | ||||
|    */ | ||||
|   @Override | ||||
|   public void close() throws IOException | ||||
|   { | ||||
|   public void close() throws IOException { | ||||
|     Closeables.close(context, true); | ||||
|   } | ||||
|  | ||||
|   //~--- get methods ---------------------------------------------------------- | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @return | ||||
|    */ | ||||
|   @Override | ||||
|   public SvnBlameCommand getBlameCommand() | ||||
|   { | ||||
|   public SvnBlameCommand getBlameCommand() { | ||||
|     return new SvnBlameCommand(context); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @return | ||||
|    */ | ||||
|   @Override | ||||
|   public SvnBrowseCommand getBrowseCommand() | ||||
|   { | ||||
|   public SvnBrowseCommand getBrowseCommand() { | ||||
|     return new SvnBrowseCommand(context); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @return | ||||
|    */ | ||||
|   @Override | ||||
|   public BundleCommand getBundleCommand() | ||||
|   { | ||||
|   public BundleCommand getBundleCommand() { | ||||
|     return new SvnBundleCommand(context); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @return | ||||
|    */ | ||||
|   @Override | ||||
|   public SvnCatCommand getCatCommand() | ||||
|   { | ||||
|   public SvnCatCommand getCatCommand() { | ||||
|     return new SvnCatCommand(context); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @return | ||||
|    */ | ||||
|   @Override | ||||
|   public SvnDiffCommand getDiffCommand() | ||||
|   { | ||||
|   public SvnDiffCommand getDiffCommand() { | ||||
|     return new SvnDiffCommand(context); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @return | ||||
|    */ | ||||
|   @Override | ||||
|   public SvnLogCommand getLogCommand() | ||||
|   { | ||||
|   public SvnLogCommand getLogCommand() { | ||||
|     return new SvnLogCommand(context); | ||||
|   } | ||||
|  | ||||
| @@ -163,34 +115,13 @@ public class SvnRepositoryServiceProvider extends RepositoryServiceProvider | ||||
|     return new SvnLookupCommand(context); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @return | ||||
|    */ | ||||
|   @Override | ||||
|   public Set<Command> getSupportedCommands() | ||||
|   { | ||||
|   public Set<Command> getSupportedCommands() { | ||||
|     return COMMANDS; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @return | ||||
|    */ | ||||
|   @Override | ||||
|   public UnbundleCommand getUnbundleCommand() | ||||
|   { | ||||
|     return new SvnUnbundleCommand(context); | ||||
|   public UnbundleCommand getUnbundleCommand() { | ||||
|     return new SvnUnbundleCommand(context, hookContextFactory, eventBus, new SvnLogCommand(context)); | ||||
|   } | ||||
|  | ||||
|   //~--- fields --------------------------------------------------------------- | ||||
|  | ||||
|   /** Field description */ | ||||
|   private final SvnContext context; | ||||
|  | ||||
|   private final SvnWorkingCopyFactory workingCopyFactory; | ||||
| } | ||||
|   | ||||
| @@ -25,21 +25,31 @@ | ||||
| package sonia.scm.repository.spi; | ||||
|  | ||||
| import com.google.inject.Inject; | ||||
| import sonia.scm.event.ScmEventBus; | ||||
| import sonia.scm.plugin.Extension; | ||||
| import sonia.scm.repository.Repository; | ||||
| import sonia.scm.repository.SvnRepositoryHandler; | ||||
| import sonia.scm.repository.SvnWorkingCopyFactory; | ||||
| import sonia.scm.repository.api.HookContextFactory; | ||||
|  | ||||
| @Extension | ||||
| public class SvnRepositoryServiceResolver implements RepositoryServiceResolver { | ||||
|  | ||||
|   private SvnRepositoryHandler handler; | ||||
|   private SvnWorkingCopyFactory workingCopyFactory; | ||||
|   private final SvnRepositoryHandler handler; | ||||
|   private final SvnWorkingCopyFactory workingCopyFactory; | ||||
|   private final HookContextFactory hookContextFactory; | ||||
|   private final ScmEventBus eventBus; | ||||
|  | ||||
|   @Inject | ||||
|   public SvnRepositoryServiceResolver(SvnRepositoryHandler handler, SvnWorkingCopyFactory workingCopyFactory) { | ||||
|   public SvnRepositoryServiceResolver(SvnRepositoryHandler handler, | ||||
|                                       SvnWorkingCopyFactory workingCopyFactory, | ||||
|                                       HookContextFactory hookContextFactory, | ||||
|                                       ScmEventBus eventBus | ||||
|   ) { | ||||
|     this.handler = handler; | ||||
|     this.workingCopyFactory = workingCopyFactory; | ||||
|     this.hookContextFactory = hookContextFactory; | ||||
|     this.eventBus = eventBus; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
| @@ -47,7 +57,7 @@ public class SvnRepositoryServiceResolver implements RepositoryServiceResolver { | ||||
|     SvnRepositoryServiceProvider provider = null; | ||||
|  | ||||
|     if (SvnRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) { | ||||
|       provider = new SvnRepositoryServiceProvider(handler, repository, workingCopyFactory); | ||||
|       provider = new SvnRepositoryServiceProvider(handler, repository, workingCopyFactory, hookContextFactory, eventBus); | ||||
|     } | ||||
|  | ||||
|     return provider; | ||||
|   | ||||
| @@ -24,82 +24,74 @@ | ||||
|  | ||||
| package sonia.scm.repository.spi; | ||||
|  | ||||
| //~--- non-JDK imports -------------------------------------------------------- | ||||
|  | ||||
| import com.google.common.collect.ImmutableSet; | ||||
| import com.google.common.io.ByteSource; | ||||
| import com.google.common.io.Closeables; | ||||
|  | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import org.tmatesoft.svn.core.SVNException; | ||||
| import org.tmatesoft.svn.core.wc.SVNClientManager; | ||||
| import org.tmatesoft.svn.core.wc.admin.SVNAdminClient; | ||||
|  | ||||
| import sonia.scm.ContextEntry; | ||||
| import sonia.scm.event.ScmEventBus; | ||||
| import sonia.scm.repository.Changeset; | ||||
| import sonia.scm.repository.PostReceiveRepositoryHookEvent; | ||||
| import sonia.scm.repository.Repository; | ||||
| import sonia.scm.repository.RepositoryHookEvent; | ||||
| import sonia.scm.repository.SvnUtil; | ||||
| import sonia.scm.repository.api.HookContext; | ||||
| import sonia.scm.repository.api.HookContextFactory; | ||||
| import sonia.scm.repository.api.HookFeature; | ||||
| import sonia.scm.repository.api.ImportFailedException; | ||||
| import sonia.scm.repository.api.UnbundleResponse; | ||||
|  | ||||
| import static com.google.common.base.Preconditions.*; | ||||
|  | ||||
| //~--- JDK imports ------------------------------------------------------------ | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.util.Iterator; | ||||
| import java.util.List; | ||||
| import java.util.NoSuchElementException; | ||||
| import java.util.Set; | ||||
| import java.util.concurrent.Callable; | ||||
|  | ||||
| import static com.google.common.base.Preconditions.checkNotNull; | ||||
| import static sonia.scm.repository.RepositoryHookType.POST_RECEIVE; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author Sebastian Sdorra <s.sdorra@gmail.com> | ||||
|  */ | ||||
| public class SvnUnbundleCommand extends AbstractSvnCommand | ||||
|   implements UnbundleCommand | ||||
| { | ||||
| public class SvnUnbundleCommand extends AbstractSvnCommand implements UnbundleCommand { | ||||
|  | ||||
|   /** Field description */ | ||||
|   private static final Logger logger = | ||||
|     LoggerFactory.getLogger(SvnUnbundleCommand.class); | ||||
|   private static final Logger LOG = LoggerFactory.getLogger(SvnUnbundleCommand.class); | ||||
|   private final HookContextFactory hookContextFactory; | ||||
|   private final ScmEventBus eventBus; | ||||
|   private final SvnLogCommand svnLogCommand; | ||||
|  | ||||
|   //~--- constructors --------------------------------------------------------- | ||||
|  | ||||
|   /** | ||||
|    * Constructs ... | ||||
|    * | ||||
|    *  @param context | ||||
|    * | ||||
|    */ | ||||
|   public SvnUnbundleCommand(SvnContext context) | ||||
|   { | ||||
|   public SvnUnbundleCommand(SvnContext context, | ||||
|                             HookContextFactory hookContextFactory, | ||||
|                             ScmEventBus eventBus, | ||||
|                             SvnLogCommand svnLogCommand | ||||
|   ) { | ||||
|     super(context); | ||||
|     this.hookContextFactory = hookContextFactory; | ||||
|     this.eventBus = eventBus; | ||||
|     this.svnLogCommand = svnLogCommand; | ||||
|   } | ||||
|  | ||||
|   //~--- methods -------------------------------------------------------------- | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @param request | ||||
|    * | ||||
|    * @return | ||||
|    * | ||||
|    * @throws IOException | ||||
|    */ | ||||
|   @Override | ||||
|   public UnbundleResponse unbundle(UnbundleCommandRequest request) | ||||
|     throws IOException | ||||
|   { | ||||
|     throws IOException { | ||||
|     ByteSource archive = checkNotNull(request.getArchive(), | ||||
|                            "archive is required"); | ||||
|       "archive is required"); | ||||
|  | ||||
|     logger.debug("archive repository {} to {}", context.getDirectory(), | ||||
|     LOG.debug("archive repository {} to {}", context.getDirectory(), | ||||
|       archive); | ||||
|  | ||||
|     UnbundleResponse response; | ||||
|  | ||||
|     SVNClientManager clientManager = null; | ||||
|  | ||||
|     try | ||||
|     { | ||||
|     try { | ||||
|       clientManager = SVNClientManager.newInstance(); | ||||
|  | ||||
|       SVNAdminClient adminClient = clientManager.getAdminClient(); | ||||
| @@ -107,44 +99,124 @@ public class SvnUnbundleCommand extends AbstractSvnCommand | ||||
|       restore(adminClient, archive, context.getDirectory()); | ||||
|  | ||||
|       response = new UnbundleResponse(context.open().getLatestRevision()); | ||||
|     } | ||||
|     catch (SVNException ex) | ||||
|     { | ||||
|     } catch (SVNException ex) { | ||||
|       throw new IOException("could not restore dump", ex); | ||||
|     } | ||||
|     finally | ||||
|     { | ||||
|     } finally { | ||||
|       SvnUtil.dispose(clientManager); | ||||
|     } | ||||
|  | ||||
|     firePostReceiveRepositoryHookEvent(); | ||||
|     return response; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @param adminClient | ||||
|    * @param dump | ||||
|    * @param repository | ||||
|    * | ||||
|    * @throws IOException | ||||
|    * @throws SVNException | ||||
|    */ | ||||
|   private void restore(SVNAdminClient adminClient, ByteSource dump, | ||||
|     File repository) | ||||
|     throws SVNException, IOException | ||||
|   { | ||||
|   private void firePostReceiveRepositoryHookEvent() { | ||||
|     eventBus.post(createEvent()); | ||||
|   } | ||||
|  | ||||
|   private PostReceiveRepositoryHookEvent createEvent() { | ||||
|     Repository repository = this.context.getRepository(); | ||||
|     HookContext context = hookContextFactory.createContext(new SvnImportHookContextProvider(repository, svnLogCommand), repository); | ||||
|     RepositoryHookEvent repositoryHookEvent = new RepositoryHookEvent(context, repository, POST_RECEIVE); | ||||
|     return new PostReceiveRepositoryHookEvent(repositoryHookEvent); | ||||
|   } | ||||
|  | ||||
|   private void restore(SVNAdminClient adminClient, ByteSource dump, File repository) throws SVNException, IOException { | ||||
|     InputStream inputStream = null; | ||||
|  | ||||
|     try | ||||
|     { | ||||
|     try { | ||||
|       inputStream = dump.openBufferedStream(); | ||||
|       adminClient.doLoad(repository, inputStream); | ||||
|     } | ||||
|     finally | ||||
|     { | ||||
|     } finally { | ||||
|       Closeables.close(inputStream, true); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private static class SvnImportHookContextProvider extends HookContextProvider { | ||||
|  | ||||
|     private final Repository repository; | ||||
|     private final LogCommand logCommand; | ||||
|  | ||||
|     private SvnImportHookContextProvider(Repository repository, SvnLogCommand logCommand) { | ||||
|       this.repository = repository; | ||||
|       this.logCommand = logCommand; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Set<HookFeature> getSupportedFeatures() { | ||||
|       return ImmutableSet.of(HookFeature.CHANGESET_PROVIDER); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public HookChangesetProvider getChangesetProvider() { | ||||
|       ChangesetResolver changesetResolver = new ChangesetResolver(repository, logCommand); | ||||
|       return r -> new HookChangesetResponse(changesetResolver.call()); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private static class ChangesetResolver implements Callable<Iterable<Changeset>> { | ||||
|  | ||||
|     private final Repository repository; | ||||
|     private final LogCommand logCommand; | ||||
|  | ||||
|     ChangesetResolver(Repository repository, LogCommand logCommand) { | ||||
|       this.repository = repository; | ||||
|       this.logCommand = logCommand; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Iterable<Changeset> call() { | ||||
|       return SingleLogRequestChangesetIterator::new; | ||||
|     } | ||||
|  | ||||
|     private class SingleLogRequestChangesetIterator implements Iterator<Changeset> { | ||||
|  | ||||
|       private int currentNumber = 0; | ||||
|       private Changeset nextChangeset; | ||||
|  | ||||
|       SingleLogRequestChangesetIterator() { | ||||
|         prefetch(); | ||||
|       } | ||||
|  | ||||
|       @Override | ||||
|       public boolean hasNext() { | ||||
|         return nextChangeset != null; | ||||
|       } | ||||
|  | ||||
|       @Override | ||||
|       public Changeset next() { | ||||
|         if (nextChangeset == null) { | ||||
|           throw new NoSuchElementException(); | ||||
|         } | ||||
|         Changeset currentChangeset = nextChangeset; | ||||
|         prefetch(); | ||||
|         return currentChangeset; | ||||
|       } | ||||
|  | ||||
|       private void prefetch() { | ||||
|         try { | ||||
|           List<Changeset> changesets = fetchSingleChangesetPage(); | ||||
|           if (changesets.isEmpty()) { | ||||
|             nextChangeset = null; | ||||
|           } else { | ||||
|             nextChangeset = changesets.get(0); | ||||
|           } | ||||
|         } catch (IOException e) { | ||||
|           throw new ImportFailedException( | ||||
|             ContextEntry.ContextBuilder.entity(repository).build(), | ||||
|             "Could not provide changeset nr " + currentNumber + " for imported repository", | ||||
|             e | ||||
|           ); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       private List<Changeset> fetchSingleChangesetPage() throws IOException { | ||||
|         LogCommandRequest request = new LogCommandRequest(); | ||||
|         request.setPagingStart(currentNumber); | ||||
|         request.setPagingLimit(1); | ||||
|         List<Changeset> changesets = logCommand.getChangesets(request).getChangesets(); | ||||
|         currentNumber = currentNumber + 1; | ||||
|         return changesets; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -24,77 +24,101 @@ | ||||
|  | ||||
| package sonia.scm.repository.spi; | ||||
|  | ||||
| //~--- non-JDK imports -------------------------------------------------------- | ||||
|  | ||||
| import com.google.common.collect.ImmutableList; | ||||
| import com.google.common.io.Files; | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
| import org.mockito.ArgumentCaptor; | ||||
| import org.mockito.Captor; | ||||
| import org.tmatesoft.svn.core.SVNException; | ||||
| import org.tmatesoft.svn.core.io.SVNRepository; | ||||
| import org.tmatesoft.svn.core.io.SVNRepositoryFactory; | ||||
| import sonia.scm.event.ScmEventBus; | ||||
| import sonia.scm.repository.Changeset; | ||||
| import sonia.scm.repository.Person; | ||||
| import sonia.scm.repository.PostReceiveRepositoryHookEvent; | ||||
| import sonia.scm.repository.Repository; | ||||
| import sonia.scm.repository.RepositoryTestData; | ||||
| import sonia.scm.repository.SvnUtil; | ||||
| import sonia.scm.repository.api.HookChangesetBuilder; | ||||
| import sonia.scm.repository.api.HookContext; | ||||
| import sonia.scm.repository.api.HookContextFactory; | ||||
| import sonia.scm.repository.api.UnbundleResponse; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.util.List; | ||||
|  | ||||
| import static org.hamcrest.Matchers.is; | ||||
| import static org.hamcrest.Matchers.notNullValue; | ||||
| import static org.junit.Assert.assertThat; | ||||
| import static org.assertj.core.api.Assertions.assertThat; | ||||
| import static org.mockito.ArgumentMatchers.any; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.mock; | ||||
| import static org.mockito.Mockito.verify; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| //~--- JDK imports ------------------------------------------------------------ | ||||
| public class SvnUnbundleCommandTest extends AbstractSvnCommandTestBase { | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author Sebastian Sdorra | ||||
|  */ | ||||
| public class SvnUnbundleCommandTest extends AbstractSvnCommandTestBase | ||||
| { | ||||
|   private final Repository repository = RepositoryTestData.createHeartOfGold("svn"); | ||||
|   private HookContextFactory hookContextFactory; | ||||
|   private ScmEventBus eventBus; | ||||
|   private SvnLogCommand logCommand; | ||||
|   private HookChangesetBuilder hookChangesetBuilder; | ||||
|  | ||||
|   @Captor | ||||
|   private final ArgumentCaptor<PostReceiveRepositoryHookEvent> eventCaptor = | ||||
|     ArgumentCaptor.forClass(PostReceiveRepositoryHookEvent.class); | ||||
|  | ||||
|   @Before | ||||
|   public void initMocks() { | ||||
|     hookContextFactory = mock(HookContextFactory.class); | ||||
|     eventBus = mock(ScmEventBus.class); | ||||
|     logCommand = mock(SvnLogCommand.class); | ||||
|     HookContext hookContext = mock(HookContext.class); | ||||
|     hookChangesetBuilder = mock(HookChangesetBuilder.class); | ||||
|     when(hookContextFactory.createContext(any(), eq(repository))).thenReturn(hookContext); | ||||
|     when(hookContext.getChangesetProvider()).thenReturn(hookChangesetBuilder); | ||||
|   } | ||||
|  | ||||
|   @Test | ||||
|   public void testUnbundle() | ||||
|     throws IOException, SVNException | ||||
|   { | ||||
|   public void shouldFirePostCommitEventAfterUnbundle() throws IOException, SVNException { | ||||
|     Changeset first = new Changeset("1", 0L, new Person("trillian"), "first commit"); | ||||
|     when(hookChangesetBuilder.getChangesetList()).thenReturn(ImmutableList.of(first)); | ||||
|  | ||||
|     File bundle = bundle(); | ||||
|     SvnContext ctx = createEmptyContext(); | ||||
|     //J- | ||||
|     UnbundleResponse res = new SvnUnbundleCommand(ctx) | ||||
|     UnbundleResponse res = new SvnUnbundleCommand(ctx, hookContextFactory, eventBus, logCommand) | ||||
|       .unbundle(new UnbundleCommandRequest(Files.asByteSource(bundle)) | ||||
|     ); | ||||
|       ); | ||||
|     //J+ | ||||
|  | ||||
|     assertThat(res, notNullValue()); | ||||
|     assertThat(res.getChangesetCount(), is(5l)); | ||||
|  | ||||
|     assertThat(res).isNotNull(); | ||||
|     assertThat(res.getChangesetCount()).isEqualTo(5); | ||||
|     SVNRepository repo = ctx.open(); | ||||
|     assertThat(repo.getLatestRevision()).isEqualTo(5); | ||||
|  | ||||
|     verify(eventBus).post(eventCaptor.capture()); | ||||
|     PostReceiveRepositoryHookEvent event = eventCaptor.getValue(); | ||||
|     List<Changeset> changesets = event.getContext().getChangesetProvider().getChangesetList(); | ||||
|     assertThat(changesets).hasSize(1); | ||||
|     assertThat(changesets).contains(first); | ||||
|  | ||||
|     assertThat(repo.getLatestRevision(), is(5l)); | ||||
|     SvnUtil.closeSession(repo); | ||||
|   } | ||||
|  | ||||
|   private File bundle() throws IOException | ||||
|   { | ||||
|   private File bundle() throws IOException { | ||||
|     File file = tempFolder.newFile(); | ||||
|  | ||||
|     //J- | ||||
|     new SvnBundleCommand(createContext()) | ||||
|       .bundle(new BundleCommandRequest(Files.asByteSink(file)) | ||||
|     ); | ||||
|       ); | ||||
|     //J+ | ||||
|  | ||||
|     return file; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|    * | ||||
|    * @return | ||||
|    * | ||||
|    * @throws IOException | ||||
|    * @throws SVNException | ||||
|    */ | ||||
|   private SvnContext createEmptyContext() throws IOException, SVNException | ||||
|   { | ||||
|   private SvnContext createEmptyContext() throws IOException, SVNException { | ||||
|     File folder = tempFolder.newFolder(); | ||||
|  | ||||
|     SVNRepositoryFactory.createLocalRepository(folder, true, true); | ||||
|   | ||||
| @@ -0,0 +1 @@ | ||||
| mock-maker-inline | ||||
		Reference in New Issue
	
	Block a user