Refactor RefUpdateHandler
Split the logic from RefUpdateHandler into handling a separate
EventHandler to support injecting update requests from outside the
GitReferenceUpdatedListener.
Change-Id: I19cdb1dadce43d7461f4df04884e6e5dc1c66afc
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/EventHandler.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/EventHandler.java
new file mode 100644
index 0000000..5925ec0
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/EventHandler.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.repositoryuse;
+
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+
+import com.google.inject.Inject;
+
+public class EventHandler implements GitReferenceUpdatedListener {
+ RefUpdateHandlerFactory refUpdateHandlerFactory;
+
+ @Inject
+ public EventHandler(RefUpdateHandlerFactory refUpdateHandlerFactory) {
+ this.refUpdateHandlerFactory = refUpdateHandlerFactory;
+ }
+
+ @Override
+ public void onGitReferenceUpdated(Event event) {
+ RefUpdate update = new RefUpdate(event);
+ refUpdateHandlerFactory.create(update).run();
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/Module.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/Module.java
index 4c7b822..1a97a63 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/Module.java
@@ -20,16 +20,20 @@
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.internal.UniqueAnnotations;
public class Module extends AbstractModule {
@Override
protected void configure() {
DynamicSet.bind(binder(), GitReferenceUpdatedListener.class)
- .to(RefUpdateHandler.class);
+ .to(EventHandler.class);
requestStaticInjection(Config.class);
requestStaticInjection(Ref.Table.class);
requestStaticInjection(Usage.Table.class);
+ install(new FactoryModuleBuilder()
+ .implement(RefUpdateHandler.class, RefUpdateHandlerImpl.class)
+ .build(RefUpdateHandlerFactory.class));
bind(LifecycleListener.class).annotatedWith(UniqueAnnotations.create())
.to(SQLDriver.class);
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdate.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdate.java
new file mode 100644
index 0000000..a9a3923
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdate.java
@@ -0,0 +1,73 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.repositoryuse;
+
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+
+import org.eclipse.jgit.lib.ObjectId;
+
+public class RefUpdate {
+ private String projectName;
+ private String refName;
+ private String oldObjectId;
+ private String newObjectId;
+ private boolean isCreate;
+ private boolean isDelete;
+
+ public RefUpdate(String projectName, String refName, String oldObjectId,
+ String newObjectId) {
+ this.projectName = projectName;
+ this.refName = refName;
+ this.oldObjectId = oldObjectId;
+ this.newObjectId = newObjectId;
+ this.isCreate = oldObjectId.equals(ObjectId.zeroId().name());
+ this.isDelete = newObjectId.equals(ObjectId.zeroId().name());
+ }
+
+ public RefUpdate(GitReferenceUpdatedListener.Event event) {
+ this.projectName = event.getProjectName();
+ this.refName = event.getRefName();
+ this.oldObjectId = event.getOldObjectId();
+ this.newObjectId = event.getNewObjectId();
+ // TODO: could use event.isCreate() / isDelete() hree, but keep
+ // some compatibility with Gerrit 2.11
+ this.isCreate = oldObjectId.equals(ObjectId.zeroId().name());
+ this.isDelete = newObjectId.equals(ObjectId.zeroId().name());
+ }
+
+ public String getProjectName() {
+ return projectName;
+ }
+
+ public String getRefName() {
+ return refName;
+ }
+
+ public String getOldObjectId() {
+ return oldObjectId;
+ }
+
+ public String getNewObjectId() {
+ return newObjectId;
+ }
+
+ public boolean isCreate() {
+ return isCreate;
+ }
+
+ public boolean isDelete() {
+ return isDelete;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandler.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandler.java
index f3685b2..e56417d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandler.java
@@ -14,279 +14,6 @@
package com.googlesource.gerrit.plugins.repositoryuse;
-import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.inject.Inject;
-
-import org.eclipse.jgit.diff.DiffEntry;
-import org.eclipse.jgit.diff.DiffFormatter;
-import org.eclipse.jgit.diff.RawTextComparator;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.ObjectReader;
-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.submodule.SubmoduleWalk;
-import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.util.io.DisabledOutputStream;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class RefUpdateHandler implements GitReferenceUpdatedListener {
-
- private static final Logger log =
- LoggerFactory.getLogger(RefUpdateHandler.class);
- private final GitRepositoryManager repoManager;
- private final String serverName;
-
- @Inject
- RefUpdateHandler(GitRepositoryManager repoManager,
- @CanonicalWebUrl String canonicalWebUrl) {
- this.repoManager = repoManager;
- if (canonicalWebUrl != null) {
- try {
- URL url = new URL(canonicalWebUrl);
- canonicalWebUrl = url.getHost();
- } catch (MalformedURLException e) {
- log.warn("Could not parse canonicalWebUrl", e);
- }
- }
- this.serverName = canonicalWebUrl;
- }
-
- @Override
- public void onGitReferenceUpdated(Event event) {
- if (event.isDelete() && event.getRefName().startsWith(Constants.R_HEADS)
- || event.getRefName().startsWith(Constants.R_TAGS)) {
- // Ref was deleted... clean up any references
- Ref ref = Ref.fetchByRef(event.getProjectName(), event.getRefName());
- if (ref != null) {
- ref.delete();
- }
- if (event.getRefName().startsWith(Constants.R_HEADS)) {
- // Also clean up uses from this ref
- Usage.deleteByBranch(getCanonicalProject(event.getProjectName()),
- event.getRefName());
- }
- } else if (event.getRefName().startsWith(Constants.R_TAGS)) {
- Ref updatedRef = new Ref(event.getProjectName(), event.getRefName(),
- event.getNewObjectId());
- updatedRef.save();
- } else if (event.getRefName().startsWith(Constants.R_HEADS)) {
- Ref updatedRef = new Ref(event.getProjectName(), event.getRefName(),
- event.getNewObjectId());
- updatedRef.save();
- Project.NameKey nameKey = new Project.NameKey(event.getProjectName());
- try {
- if (Config.refreshAllSubmodules() || event.isCreate()
- || isSubmoduleUpdate(event, nameKey)) {
- Map<String, String> submodules = getSubmodules(event, nameKey);
- updateProjects(event.getProjectName(), event.getRefName(),
- submodules);
- }
- if (Config.parseManifests()) {
- parseManifests(event, nameKey);
- }
- } catch (IOException e) {
- log.error(e.getMessage(), e);
- }
- }
- }
-
- private void parseManifests(Event event, Project.NameKey project)
- throws RepositoryNotFoundException, IOException {
- if (event.isDelete()) {
- return;
- }
- try (Repository repo = repoManager.openRepository(project)) {
- try (RevWalk walk = new RevWalk(repo); TreeWalk tw = new TreeWalk(repo)) {
- RevCommit commit =
- walk.parseCommit(repo.resolve(event.getNewObjectId()));
-
- tw.setRecursive(false);
- tw.addTree(commit.getTree());
- ObjectReader or = tw.getObjectReader();
- while (tw.next()) {
- String path = tw.getPathString();
- if (path.endsWith(".xml")) {
- ManifestParser mp = new ManifestParser();
- ObjectLoader ol = or.open(tw.getObjectId(0));
- if (!ol.isLarge()) {
- Map<String, String> tmp = mp.parseManifest(ol.getBytes());
- HashMap<String, String> projects = new HashMap<>();
- for (String key : tmp.keySet()) {
- projects
- .put(
- normalizePath(String.format("%s:%s",
- event.getProjectName(), path), key, true),
- tmp.get(key));
- }
- updateProjects(
- String.format("%s:%s", event.getProjectName(), path),
- event.getRefName(), projects);
- } else {
- log.warn(String.format("%s is too large, skipping manifest parse",
- tw.getPathString()));
- }
- }
- }
- }
- }
- }
-
- /**
- * Has a submodule been updated?
- *
- * @param event the Event
- * @return True if a submodule update occurred, otherwise False.
- */
- private boolean isSubmoduleUpdate(Event event, Project.NameKey project)
- throws RepositoryNotFoundException, IOException {
- if (event.isDelete()) {
- return false;
- }
- try (Repository repo = repoManager.openRepository(project)) {
- try (RevWalk walk = new RevWalk(repo);
- DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE)) {
- RevTree aTree = null;
- if (!event.isCreate()) {
- // If this is a new ref, we can't get the original commit.
- // We can still use the DiffFormatter to give us what changed
- // by passing null, however.
- RevCommit aCommit =
- walk.parseCommit(repo.resolve(event.getOldObjectId()));
- aTree = aCommit.getTree();
- }
- RevCommit bCommit =
- walk.parseCommit(repo.resolve(event.getNewObjectId()));
- RevTree bTree = bCommit.getTree();
-
- df.setRepository(repo);
- df.setDiffComparator(RawTextComparator.DEFAULT);
- df.setDetectRenames(true);
- List<DiffEntry> diffEntries = df.scan(aTree, bTree);
- for (DiffEntry de : diffEntries) {
- FileMode oldMode = de.getOldMode();
- FileMode newMode = de.getNewMode();
- if ((oldMode != null && oldMode == FileMode.GITLINK)
- || (newMode != null && newMode == FileMode.GITLINK)) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- private Map<String, String> getSubmodules(Event event,
- Project.NameKey project) throws RepositoryNotFoundException, IOException {
- HashMap<String, String> submodules = new HashMap<>();
- try (Repository repo = repoManager.openRepository(project)) {
- try (RevWalk walk = new RevWalk(repo);
- SubmoduleWalk sw = new SubmoduleWalk(repo)) {
- RevCommit commit =
- walk.parseCommit(repo.resolve(event.getNewObjectId()));
- sw.setTree(commit.getTree());
- sw.setRootTree(commit.getTree());
- while (sw.next()) {
- submodules.put(
- normalizePath(event.getProjectName(), sw.getModulesUrl(), false),
- sw.getObjectId().name());
- }
- } catch (ConfigInvalidException e) {
- log.warn("Invalid .gitmodules configuration while parsing "
- + event.getProjectName());
- }
- }
- return submodules;
- }
-
- private void updateProjects(String project, String branch,
- Map<String, String> projects) {
- String canonicalProject = getCanonicalProject(project);
- List<Usage> uses = Usage.fetchByProject(canonicalProject, branch);
- for (Usage use : uses) {
- if (!projects.containsKey(use.getDestination())) {
- // No longer exists; delete.
- use.delete();
- } else {
- // Update SHA1 here.
- use.setRef(projects.get(use.getDestination()));
- use.save();
- projects.remove(use.getDestination());
- }
- }
- // At this point, submodules only contains new elements.
- // Create them.
- for (String key : projects.keySet()) {
- Usage use = new Usage(canonicalProject, branch, key, projects.get(key));
- use.save();
- }
- }
-
- private String getCanonicalProject(String project) {
- String canonicalProject =
- String.format("https://%s/%s", serverName, project);
- try {
- URL url = new URL(canonicalProject);
- canonicalProject = url.getHost() + url.getPath();
- } catch (MalformedURLException e) {
- log.warn("Could not parse project as URL: " + canonicalProject);
- }
- return canonicalProject;
- }
-
- private String normalizePath(String project, String destination,
- boolean isManifest) {
- String originalProject =
- isManifest ? project.substring(0, project.lastIndexOf(":")) : project;
-
- // Handle relative and absolute paths on the same server
- if (destination.startsWith("/")) {
- if (serverName != null) {
- destination = serverName + destination;
- } else {
- log.warn("Could not parse absolute path; canonicalWebUrl not set");
- }
- } else if (destination.startsWith(".")) {
- if (serverName != null) {
- Path path = Paths.get(String.format("/%s/%s", project, destination));
- destination = serverName + path.normalize().toString();
- } else {
- log.warn("Could not parse relative path; canonicalWebUrl not set");
- }
- } else if (!destination.matches("^[^:]+://.*")) {
- if (serverName != null) {
- destination = serverName + "/" + originalProject + "/" + destination;
- } else {
- log.warn("Could not parse relative path; canonicalWebURl not set");
- }
- }
-
- try {
- // Replace the protocol with a known scheme, to avoid angering URL
- destination = destination.replaceFirst("^[^:]+://", "");
- URL url = new URL("https://" + destination);
- destination = url.getHost() + url.getPath();
- } catch (MalformedURLException e) {
- log.warn("Could not parse destination as URL: " + destination);
- }
- return destination;
- }
+public interface RefUpdateHandler {
+ void run();
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandlerFactory.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandlerFactory.java
new file mode 100644
index 0000000..8c8cedb
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandlerFactory.java
@@ -0,0 +1,19 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.repositoryuse;
+
+public interface RefUpdateHandlerFactory {
+ RefUpdateHandler create(RefUpdate update);
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandlerImpl.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandlerImpl.java
new file mode 100644
index 0000000..638c267
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandlerImpl.java
@@ -0,0 +1,295 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.repositoryuse;
+
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.diff.DiffFormatter;
+import org.eclipse.jgit.diff.RawTextComparator;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+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.submodule.SubmoduleWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.io.DisabledOutputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RefUpdateHandlerImpl implements RefUpdateHandler {
+ private static final Logger log =
+ LoggerFactory.getLogger(RefUpdateHandlerImpl.class);
+
+ private RefUpdate event;
+ private final GitRepositoryManager repoManager;
+ private final String serverName;
+
+ @Inject
+ public RefUpdateHandlerImpl(@Assisted RefUpdate event,
+ GitRepositoryManager repoManager,
+ @CanonicalWebUrl String canonicalWebUrl) {
+ this.event = event;
+ this.repoManager = repoManager;
+ if (canonicalWebUrl != null) {
+ try {
+ URL url = new URL(canonicalWebUrl);
+ canonicalWebUrl = url.getHost();
+ } catch (MalformedURLException e) {
+ log.warn("Could not parse canonicalWebUrl", e);
+ }
+ }
+ this.serverName = canonicalWebUrl;
+ }
+
+ @Override
+ public void run() {
+ if (event.isDelete() && event.getRefName().startsWith(Constants.R_HEADS)
+ || event.getRefName().startsWith(Constants.R_TAGS)) {
+ // Ref was deleted... clean up any references
+ Ref ref = Ref.fetchByRef(event.getProjectName(), event.getRefName());
+ if (ref != null) {
+ ref.delete();
+ }
+ if (event.getRefName().startsWith(Constants.R_HEADS)) {
+ // Also clean up uses from this ref
+ Usage.deleteByBranch(getCanonicalProject(event.getProjectName()),
+ event.getRefName());
+ }
+ } else if (event.getRefName().startsWith(Constants.R_TAGS)) {
+ Ref updatedRef = new Ref(event.getProjectName(), event.getRefName(),
+ event.getNewObjectId());
+ updatedRef.save();
+ } else if (event.getRefName().startsWith(Constants.R_HEADS)) {
+ Ref updatedRef = new Ref(event.getProjectName(), event.getRefName(),
+ event.getNewObjectId());
+ updatedRef.save();
+ Project.NameKey nameKey = new Project.NameKey(event.getProjectName());
+ try {
+ if (Config.refreshAllSubmodules() || event.isCreate()
+ || isSubmoduleUpdate(event, nameKey)) {
+ Map<String, String> submodules = getSubmodules(event, nameKey);
+ updateProjects(event.getProjectName(), event.getRefName(),
+ submodules);
+ }
+ if (Config.parseManifests()) {
+ parseManifests(event, nameKey);
+ }
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+ }
+
+ private void parseManifests(RefUpdate event, Project.NameKey project)
+ throws RepositoryNotFoundException, IOException {
+ if (event.isDelete()) {
+ return;
+ }
+ try (Repository repo = repoManager.openRepository(project)) {
+ try (RevWalk walk = new RevWalk(repo); TreeWalk tw = new TreeWalk(repo)) {
+ RevCommit commit =
+ walk.parseCommit(repo.resolve(event.getNewObjectId()));
+
+ tw.setRecursive(false);
+ tw.addTree(commit.getTree());
+ ObjectReader or = tw.getObjectReader();
+ while (tw.next()) {
+ String path = tw.getPathString();
+ if (path.endsWith(".xml")) {
+ ManifestParser mp = new ManifestParser();
+ ObjectLoader ol = or.open(tw.getObjectId(0));
+ if (!ol.isLarge()) {
+ Map<String, String> tmp = mp.parseManifest(ol.getBytes());
+ HashMap<String, String> projects = new HashMap<>();
+ for (String key : tmp.keySet()) {
+ projects
+ .put(
+ normalizePath(String.format("%s:%s",
+ event.getProjectName(), path), key, true),
+ tmp.get(key));
+ }
+ updateProjects(
+ String.format("%s:%s", event.getProjectName(), path),
+ event.getRefName(), projects);
+ } else {
+ log.warn(String.format("%s is too large, skipping manifest parse",
+ tw.getPathString()));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Has a submodule been updated?
+ *
+ * @param event the Event
+ * @return True if a submodule update occurred, otherwise False.
+ */
+ private boolean isSubmoduleUpdate(RefUpdate event, Project.NameKey project)
+ throws RepositoryNotFoundException, IOException {
+ if (event.isDelete()) {
+ return false;
+ }
+ try (Repository repo = repoManager.openRepository(project)) {
+ try (RevWalk walk = new RevWalk(repo);
+ DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE)) {
+ RevTree aTree = null;
+ if (!event.isCreate()) {
+ // If this is a new ref, we can't get the original commit.
+ // We can still use the DiffFormatter to give us what changed
+ // by passing null, however.
+ RevCommit aCommit =
+ walk.parseCommit(repo.resolve(event.getOldObjectId()));
+ aTree = aCommit.getTree();
+ }
+ RevCommit bCommit =
+ walk.parseCommit(repo.resolve(event.getNewObjectId()));
+ RevTree bTree = bCommit.getTree();
+
+ df.setRepository(repo);
+ df.setDiffComparator(RawTextComparator.DEFAULT);
+ df.setDetectRenames(true);
+ List<DiffEntry> diffEntries = df.scan(aTree, bTree);
+ for (DiffEntry de : diffEntries) {
+ FileMode oldMode = de.getOldMode();
+ FileMode newMode = de.getNewMode();
+ if ((oldMode != null && oldMode == FileMode.GITLINK)
+ || (newMode != null && newMode == FileMode.GITLINK)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private Map<String, String> getSubmodules(RefUpdate event,
+ Project.NameKey project) throws RepositoryNotFoundException, IOException {
+ HashMap<String, String> submodules = new HashMap<>();
+ try (Repository repo = repoManager.openRepository(project)) {
+ try (RevWalk walk = new RevWalk(repo);
+ SubmoduleWalk sw = new SubmoduleWalk(repo)) {
+ RevCommit commit =
+ walk.parseCommit(repo.resolve(event.getNewObjectId()));
+ sw.setTree(commit.getTree());
+ sw.setRootTree(commit.getTree());
+ while (sw.next()) {
+ submodules.put(
+ normalizePath(event.getProjectName(), sw.getModulesUrl(), false),
+ sw.getObjectId().name());
+ }
+ } catch (ConfigInvalidException e) {
+ log.warn("Invalid .gitmodules configuration while parsing "
+ + event.getProjectName());
+ }
+ }
+ return submodules;
+ }
+
+ private void updateProjects(String project, String branch,
+ Map<String, String> projects) {
+ String canonicalProject = getCanonicalProject(project);
+ List<Usage> uses = Usage.fetchByProject(canonicalProject, branch);
+ for (Usage use : uses) {
+ if (!projects.containsKey(use.getDestination())) {
+ // No longer exists; delete.
+ use.delete();
+ } else {
+ // Update SHA1 here.
+ use.setRef(projects.get(use.getDestination()));
+ use.save();
+ projects.remove(use.getDestination());
+ }
+ }
+ // At this point, submodules only contains new elements.
+ // Create them.
+ for (String key : projects.keySet()) {
+ Usage use = new Usage(canonicalProject, branch, key, projects.get(key));
+ use.save();
+ }
+ }
+
+ private String getCanonicalProject(String project) {
+ String canonicalProject =
+ String.format("https://%s/%s", serverName, project);
+ try {
+ URL url = new URL(canonicalProject);
+ canonicalProject = url.getHost() + url.getPath();
+ } catch (MalformedURLException e) {
+ log.warn("Could not parse project as URL: " + canonicalProject);
+ }
+ return canonicalProject;
+ }
+
+ private String normalizePath(String project, String destination,
+ boolean isManifest) {
+ String originalProject =
+ isManifest ? project.substring(0, project.lastIndexOf(":")) : project;
+
+ // Handle relative and absolute paths on the same server
+ if (destination.startsWith("/")) {
+ if (serverName != null) {
+ destination = serverName + destination;
+ } else {
+ log.warn("Could not parse absolute path; canonicalWebUrl not set");
+ }
+ } else if (destination.startsWith(".")) {
+ if (serverName != null) {
+ Path path = Paths.get(String.format("/%s/%s", project, destination));
+ destination = serverName + path.normalize().toString();
+ } else {
+ log.warn("Could not parse relative path; canonicalWebUrl not set");
+ }
+ } else if (!destination.matches("^[^:]+://.*")) {
+ if (serverName != null) {
+ destination = serverName + "/" + originalProject + "/" + destination;
+ } else {
+ log.warn("Could not parse relative path; canonicalWebURl not set");
+ }
+ }
+
+ try {
+ // Replace the protocol with a known scheme, to avoid angering URL
+ destination = destination.replaceFirst("^[^:]+://", "");
+ URL url = new URL("https://" + destination);
+ destination = url.getHost() + url.getPath();
+ } catch (MalformedURLException e) {
+ log.warn("Could not parse destination as URL: " + destination);
+ }
+ return destination;
+ }
+}