diff --git a/src/main/java/nl/tudelft/ewi/devhub/server/backend/BuildsBackend.java b/src/main/java/nl/tudelft/ewi/devhub/server/backend/BuildsBackend.java index 6804f9dc..0e905a89 100644 --- a/src/main/java/nl/tudelft/ewi/devhub/server/backend/BuildsBackend.java +++ b/src/main/java/nl/tudelft/ewi/devhub/server/backend/BuildsBackend.java @@ -1,5 +1,9 @@ package nl.tudelft.ewi.devhub.server.backend; +import javax.inject.Inject; +import javax.ws.rs.NotFoundException; + +import java.util.Date; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledExecutorService; @@ -7,23 +11,22 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import javax.inject.Inject; -import javax.ws.rs.NotFoundException; - +import com.google.common.base.Preconditions; +import com.google.common.collect.Queues; +import com.google.inject.Provider; +import com.google.inject.persist.Transactional; +import com.google.inject.persist.UnitOfWork; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import nl.tudelft.ewi.build.client.BuildServerBackend; import nl.tudelft.ewi.build.client.BuildServerBackendImpl; import nl.tudelft.ewi.build.jaxrs.models.BuildRequest; +import nl.tudelft.ewi.devhub.server.database.controllers.BuildResults; import nl.tudelft.ewi.devhub.server.database.controllers.BuildServers; +import nl.tudelft.ewi.devhub.server.database.entities.BuildResult; import nl.tudelft.ewi.devhub.server.database.entities.BuildServer; import nl.tudelft.ewi.devhub.server.web.errors.ApiError; -import com.google.common.base.Preconditions; -import com.google.common.collect.Queues; -import com.google.inject.Provider; -import com.google.inject.persist.Transactional; -import com.google.inject.persist.UnitOfWork; - /** * The {@link BuildServerClient} allows you to query and manipulate data from the build-server. */ @@ -32,7 +35,7 @@ public class BuildsBackend { private final BuildServers buildServers; private final Provider submitters; - private final ConcurrentLinkedQueue buildQueue; + private final ConcurrentLinkedQueue buildQueue; private final ScheduledExecutorService executor; private final AtomicBoolean running; @@ -87,9 +90,9 @@ public void deleteBuildServer(long serverId) throws ApiError { } } - public void offerBuild(BuildRequest request) { + public void offerBuild(BuildRequest request, long buildResultId) { synchronized (running) { - buildQueue.offer(request); + buildQueue.offer(new BuildRequestWithResultMapping(request, buildResultId)); if (running.compareAndSet(false, true)) { BuildSubmitter task = submitters.get(); task.initialize(this); @@ -103,18 +106,29 @@ public void shutdown() throws InterruptedException { executor.awaitTermination(1, TimeUnit.MINUTES); } + @Data + private static class BuildRequestWithResultMapping { + private final BuildRequest request; + private final long id; + } + private static class BuildSubmitter extends RunnableInUnitOfWork { private static final int NO_CAPACITY_DELAY = 5000; private final Provider buildServersProvider; + private final Provider buildResultsProvider; private BuildsBackend backend; @Inject - BuildSubmitter(Provider buildServersProvider, Provider workProvider) { + BuildSubmitter(Provider buildServersProvider, + Provider buildResultsProvider, + Provider workProvider) { + super(workProvider); this.buildServersProvider = buildServersProvider; + this.buildResultsProvider = buildResultsProvider; } void initialize(BuildsBackend backend) { @@ -124,14 +138,15 @@ void initialize(BuildsBackend backend) { @Override @Transactional protected void runInUnitOfWork() { - ConcurrentLinkedQueue buildQueue = backend.buildQueue; + ConcurrentLinkedQueue buildQueue = backend.buildQueue; AtomicBoolean running = backend.running; BuildServers buildServers = buildServersProvider.get(); + BuildResults buildResults = buildResultsProvider.get(); try { while (!buildQueue.isEmpty()) { boolean delivered = false; - BuildRequest buildRequest = buildQueue.peek(); + BuildRequestWithResultMapping buildRequestWithId = buildQueue.peek(); List allBuildServers = buildServers.listAll(); while (!allBuildServers.isEmpty()) { @@ -141,10 +156,14 @@ protected void runInUnitOfWork() { String secret = buildServer.getSecret(); BuildServerBackend backend = new BuildServerBackendImpl(host, name, secret); - if (backend.offerBuildRequest(buildRequest)) { + if (backend.offerBuildRequest(buildRequestWithId.getRequest())) { delivered = true; buildQueue.poll(); log.info("One build request was succesfully handed to: {}", name); + + BuildResult result = buildResults.find(buildRequestWithId.getId()); + result.setStarted(new Date()); + buildResults.merge(result); break; } } diff --git a/src/main/java/nl/tudelft/ewi/devhub/server/database/controllers/BuildResults.java b/src/main/java/nl/tudelft/ewi/devhub/server/database/controllers/BuildResults.java index b2939f76..6ee45f04 100644 --- a/src/main/java/nl/tudelft/ewi/devhub/server/database/controllers/BuildResults.java +++ b/src/main/java/nl/tudelft/ewi/devhub/server/database/controllers/BuildResults.java @@ -4,13 +4,12 @@ import javax.persistence.EntityManager; import javax.persistence.EntityNotFoundException; +import com.google.inject.persist.Transactional; import lombok.extern.slf4j.Slf4j; import nl.tudelft.ewi.devhub.server.database.entities.BuildResult; import nl.tudelft.ewi.devhub.server.database.entities.Group; import nl.tudelft.ewi.devhub.server.database.entities.QBuildResult; -import com.google.inject.persist.Transactional; - @Slf4j public class BuildResults extends Controller { @@ -31,6 +30,18 @@ public BuildResult find(Group group, String commitId) { } return result; } + + @Transactional + public BuildResult find(long id) { + BuildResult result = query().from(QBuildResult.buildResult) + .where(QBuildResult.buildResult.id.eq(id)) + .singleResult(QBuildResult.buildResult); + + if (result == null) { + throw new EntityNotFoundException(); + } + return result; + } @Transactional public boolean exists(Group group, String commitId) { diff --git a/src/main/java/nl/tudelft/ewi/devhub/server/database/entities/BuildResult.java b/src/main/java/nl/tudelft/ewi/devhub/server/database/entities/BuildResult.java index 82e02a72..ca1a1443 100644 --- a/src/main/java/nl/tudelft/ewi/devhub/server/database/entities/BuildResult.java +++ b/src/main/java/nl/tudelft/ewi/devhub/server/database/entities/BuildResult.java @@ -10,8 +10,9 @@ import javax.persistence.Table; import javax.validation.constraints.NotNull; -import lombok.Data; +import java.util.Date; +import lombok.Data; import org.hibernate.validator.constraints.NotEmpty; @Data @@ -21,6 +22,7 @@ public class BuildResult { public static BuildResult newBuildResult(Group group, String commit) { BuildResult result = new BuildResult(); + result.setQueued(new Date()); result.setRepository(group); result.setCommitId(commit); result.setSuccess(null); @@ -32,7 +34,7 @@ public static BuildResult newBuildResult(Group group, String commit) { @Column(name = "id") @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; - + @NotNull @ManyToOne @JoinColumn(name = "repository_id") @@ -42,6 +44,15 @@ public static BuildResult newBuildResult(Group group, String commit) { @Column(name = "commit_id") private String commitId; + @Column(name = "queued") + private Date queued; + + @Column(name = "started") + private Date started; + + @Column(name = "completed") + private Date completed; + @Column(name = "success") private Boolean success; diff --git a/src/main/java/nl/tudelft/ewi/devhub/server/web/resources/HooksResource.java b/src/main/java/nl/tudelft/ewi/devhub/server/web/resources/HooksResource.java index 6991fc27..15d60420 100644 --- a/src/main/java/nl/tudelft/ewi/devhub/server/web/resources/HooksResource.java +++ b/src/main/java/nl/tudelft/ewi/devhub/server/web/resources/HooksResource.java @@ -1,10 +1,5 @@ package nl.tudelft.ewi.devhub.server.web.resources; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.util.Locale; - import javax.inject.Inject; import javax.persistence.EntityNotFoundException; import javax.servlet.http.HttpServletRequest; @@ -16,6 +11,13 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; +import java.io.UnsupportedEncodingException; +import java.util.Date; +import java.util.Locale; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.google.inject.persist.Transactional; import lombok.Data; import lombok.extern.slf4j.Slf4j; import nl.tudelft.ewi.build.jaxrs.models.BuildRequest; @@ -34,13 +36,8 @@ import nl.tudelft.ewi.git.client.Repositories; import nl.tudelft.ewi.git.models.BranchModel; import nl.tudelft.ewi.git.models.DetailedRepositoryModel; - import org.jboss.resteasy.plugins.guice.RequestScoped; -import com.google.common.base.Joiner; -import com.google.common.collect.Lists; -import com.google.inject.persist.Transactional; - @Slf4j @RequestScoped @Path("hooks") @@ -92,6 +89,9 @@ public void onGitPush(@Context HttpServletRequest request, GitPush push) throws if (buildResults.exists(group, branch.getCommit())) { continue; } + + BuildResult buildResult = BuildResult.newBuildResult(group, branch.getCommit()); + buildResults.persist(buildResult); log.info("Submitting a build for branch: {} of repository: {}", branch.getName(), repository.getName()); @@ -103,17 +103,15 @@ public void onGitPush(@Context HttpServletRequest request, GitPush push) throws StringBuilder callbackBuilder = new StringBuilder(); callbackBuilder.append(config.getHttpUrl()); callbackBuilder.append("/hooks/build-result"); - callbackBuilder.append("?repository=" + URLEncoder.encode(repository.getName(), "UTF-8")); - callbackBuilder.append("&commit=" + URLEncoder.encode(branch.getCommit(), "UTF-8")); + callbackBuilder.append("?buildId=" + buildResult.getId()); BuildRequest buildRequest = new BuildRequest(); buildRequest.setCallbackUrl(callbackBuilder.toString()); buildRequest.setInstruction(instruction); buildRequest.setSource(source); buildRequest.setTimeout(group.getBuildTimeout()); - - buildBackend.offerBuild(buildRequest); - buildResults.persist(BuildResult.newBuildResult(group, branch.getCommit())); + + buildBackend.offerBuild(buildRequest, buildResult.getId()); } } @@ -121,33 +119,23 @@ public void onGitPush(@Context HttpServletRequest request, GitPush push) throws @Path("build-result") @RequireAuthenticatedBuildServer @Transactional - public void onBuildResult(@QueryParam("repository") String repository, @QueryParam("commit") String commit, + public void onBuildResult(@QueryParam("buildId") long buildId, nl.tudelft.ewi.build.jaxrs.models.BuildResult buildResult) throws UnsupportedEncodingException { - String repoName = URLDecoder.decode(repository, "UTF-8"); - String commitId = URLDecoder.decode(commit, "UTF-8"); - Group group = groups.findByRepoName(repoName); - - BuildResult result; try { - result = buildResults.find(group, commitId); + BuildResult result = buildResults.find(buildId); + result.setCompleted(new Date()); result.setSuccess(buildResult.getStatus() == Status.SUCCEEDED); - result.setLog(Joiner.on('\n') - .join(buildResult.getLogLines())); - + result.setLog(Joiner.on('\n').join(buildResult.getLogLines())); buildResults.merge(result); - } - catch (EntityNotFoundException e) { - result = BuildResult.newBuildResult(group, commitId); - result.setSuccess(buildResult.getStatus() == Status.SUCCEEDED); - result.setLog(Joiner.on('\n') - .join(buildResult.getLogLines())); - buildResults.persist(result); + if (!result.getSuccess()) { + mailer.sendFailedBuildResult(Lists.newArrayList(Locale.ENGLISH), result); + } } - - if (!result.getSuccess()) { - mailer.sendFailedBuildResult(Lists.newArrayList(Locale.ENGLISH), result); + catch (EntityNotFoundException e) { + log.error("Could not find build result: " + buildId + ": " + e.getMessage(), e); + return; } }