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; + } +}