diff --git a/changelog.html b/changelog.html
index e2a9533ae07bafd824fa96d64d7ab592c744e22b..86938179152cacf3b6aaad7cfd445024378fc7a0 100644
--- a/changelog.html
+++ b/changelog.html
@@ -55,7 +55,9 @@ Upcoming changes
- -
+
-
+ Handle version ranges in automatic maven module dependency wiring.
+ (issue 12735)
-
Detect bugs relating to short
Descriptor
names early.
(issue 14995 continued)
diff --git a/maven-plugin/src/main/java/hudson/maven/MavenModule.java b/maven-plugin/src/main/java/hudson/maven/MavenModule.java
index b4d337cfab359240c55fc4254d1f41a96ccc2bfe..fe6f380044067f138ba5d7f80edf7cfdd8db19ed 100644
--- a/maven-plugin/src/main/java/hudson/maven/MavenModule.java
+++ b/maven-plugin/src/main/java/hudson/maven/MavenModule.java
@@ -23,6 +23,8 @@
*/
package hudson.maven;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
import hudson.CopyOnWrite;
import hudson.Functions;
import hudson.Util;
@@ -45,7 +47,13 @@ import hudson.tasks.Maven.MavenInstallation;
import hudson.tasks.Publisher;
import hudson.util.AlternativeUiTextProvider;
import hudson.util.DescribableList;
+import jenkins.model.Jenkins;
+import org.apache.maven.project.MavenProject;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+import org.kohsuke.stapler.export.Exported;
+import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
@@ -58,15 +66,6 @@ import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.servlet.ServletException;
-
-import jenkins.model.Jenkins;
-
-import org.apache.maven.project.MavenProject;
-import org.kohsuke.stapler.StaplerRequest;
-import org.kohsuke.stapler.StaplerResponse;
-import org.kohsuke.stapler.export.Exported;
-
/**
* {@link Job} that builds projects based on Maven2.
*
@@ -105,7 +104,7 @@ public class MavenModule extends AbstractMavenProject im
private transient ModuleName moduleName;
/**
- * @see documentation in {@link PomInfo#relativePath}
+ * @see PomInfo#relativePath
*/
private String relativePath;
@@ -423,7 +422,7 @@ public class MavenModule extends AbstractMavenProject im
// to emulate the old behavior that tries to identify the upstream by ignoring the version.
// Do this by putting groupId:artifactId:UNKNOWN to the modules list, but
// ONLY if we find a such an old MavenModule in this Jenkins instance.
- boolean hasDependenciesWithUnknownVersion = hasDependenciesWithUnknownVersion();
+ final boolean hasDependenciesWithUnknownVersion = hasDependenciesWithUnknownVersion();
if (data == null) {
Map modules = new HashMap();
@@ -431,7 +430,7 @@ public class MavenModule extends AbstractMavenProject im
if(!m.isBuildable()) continue;
ModuleDependency moduleDependency = m.asDependency();
MavenModule old = modules.get(moduleDependency);
- MavenModule relevant = chooseMoreRelevantModule(old, m, moduleDependency);
+ MavenModule relevant = chooseMoreRelevantModule(old, m);
modules.put(moduleDependency, relevant);
if (hasDependenciesWithUnknownVersion) {
modules.put(moduleDependency.withUnknownVersion(),relevant);
@@ -452,9 +451,6 @@ public class MavenModule extends AbstractMavenProject im
}
}
- // In case two modules with the same name are defined, modules in the same MavenModuleSet
- // take precedence.
-
// Can lead to OOME, if remembered in the computational data and there are lot big multi-module projects
// TODO: try to use soft references to clean the heap when needed
Map myParentsModules; // = data.modulesPerParent.get(getParent());
@@ -474,24 +470,40 @@ public class MavenModule extends AbstractMavenProject im
//data.modulesPerParent.put(getParent(), myParentsModules);
//}
- // if the build style is the aggregator build, define dependencies against project,
- // not module.
- AbstractProject, ?> dest = getParent().isAggregatorStyleBuild() ? getParent() : this;
+ //Create a map of groupId:artifact id keys to modules for faster look ups in findMatchingDependentModule
+ Multimap mapModules = data.byName();
for (ModuleDependency d : dependencies) {
- MavenModule src = myParentsModules.get(d);
+ MavenModule src;
+
+ // In case two modules with the same name are defined, modules in the same MavenModuleSet
+ // take precedence.
+ src = myParentsModules.get(d);
+
+ // otherwise we can pick the module with the highest version number (within the constraint that
+ // it satisfies 'd')
if (src==null) {
- src = data.allModules.get(d);
+ Collection candidates = mapModules.get(d.getName());
+ ModuleDependency winner = d.findHighestFrom(candidates);
+ src = data.allModules.get(winner);
}
-
+
if(src!=null) {
- DependencyGraph.Dependency dep = new MavenModuleDependency(
- src.getParent().isAggregatorStyleBuild() ? src.getParent() : src,dest);
+ DependencyGraph.Dependency dep = new MavenModuleDependency(nodeOf(src),nodeOf(this));
if (!dep.pointsItself())
graph.addDependency(dep);
}
}
}
+
+ /**
+ * Determines the source/sink of the dependency from a module.
+ * This is because if the build is the aggregator build, we need to define dependencies against project,
+ * not module.
+ */
+ private static AbstractMavenProject, ?> nodeOf(MavenModule m) {
+ return m.getParent().isAggregatorStyleBuild() ? m.getParent() : m;
+ }
/**
* Returns all Maven modules in this Jenkins instance.
@@ -513,7 +525,7 @@ public class MavenModule extends AbstractMavenProject im
return false;
}
- private MavenModule chooseMoreRelevantModule(MavenModule mm1, MavenModule mm2, ModuleDependency moduleDependency) {
+ private MavenModule chooseMoreRelevantModule(MavenModule mm1, MavenModule mm2) {
if (mm1 == null) {
return mm2;
@@ -521,41 +533,37 @@ public class MavenModule extends AbstractMavenProject im
if (mm2 == null) {
return mm1;
}
-
- final MavenModule moreRelevant;
- final MavenModule lessRelevant;
- int relevancy1 = getDependencyRelevancy(mm1);
- int relevancy2 = getDependencyRelevancy(mm2);
-
- if (relevancy1 > relevancy2) {
- moreRelevant = mm1;
- lessRelevant = mm2;
- } else if (relevancy2 > relevancy1) {
- moreRelevant = mm2;
- lessRelevant = mm1;
- } else {
- // arbitrary, but reproduceable
- if (mm1.getParent().getName().compareTo(mm2.getParent().getName()) < 0) {
- moreRelevant = mm2;
- lessRelevant = mm1;
- } else { // should always mean > 0 as name is unique
- moreRelevant = mm1;
- lessRelevant = mm2;
- }
+
+ int score = mm1.getDependencyRelevancy() - mm2.getDependencyRelevancy();
+ if (score==0) {
+ // tie breaker. this is arbitrary, but reproduceable
+ score = mm1.getParent().getFullName().compareTo(mm2.getParent().getFullName());
}
-
+ assert score!=0;
+
+ final MavenModule moreRelevant, lessRelevant;
+
+ if (score>0) { moreRelevant = mm1; lessRelevant = mm2; }
+ else { moreRelevant = mm2; lessRelevant = mm1; }
+
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.finer("Choosing " + moreRelevant.getParent().getName() + " over " + lessRelevant.getParent().getName()
- + " for module " + moduleDependency.getName() + ". Relevancies: " + relevancy1 + ", " + relevancy2);
+ + " for module " + mm1.asDependency().getName() + ". Relevancies: " + mm1.getDependencyRelevancy() + ", " + mm2.getDependencyRelevancy());
}
return moreRelevant;
}
- private int getDependencyRelevancy(MavenModule mm) {
-
+ /**
+ * As a guide for automatic dependency computation,
+ * determine how much this particular build is "relevant" to other builds on this Jenkins.
+ *
+ * If the binary is being deployed, we assume the user intends the result of this build be used elsewhere,
+ * so we give a higher score.
+ */
+ private int getDependencyRelevancy() {
int relevancy = 0;
- for (String goal : Util.tokenize(mm.getGoals())) {
+ for (String goal : Util.tokenize(getGoals())) {
if ("deploy".equals(goal) || "deploy:deploy".equals(goal)) {
return 2;
}
@@ -565,7 +573,7 @@ public class MavenModule extends AbstractMavenProject im
}
}
- for (Publisher publisher : mm.getParent().getPublishers()) {
+ for (Publisher publisher : getParent().getPublishers()) {
if (publisher instanceof RedeployPublisher) {
return 2;
}
@@ -576,14 +584,32 @@ public class MavenModule extends AbstractMavenProject im
private static class MavenDependencyComputationData {
boolean withUnknownVersions = false;
- Map allModules;
+ /**
+ * All {@link MavenModule}s in this Jenkins, keyed by their {@link MavenModule#asDependency()}.
+ */
+ private final Map allModules;
//Map> modulesPerParent = new HashMap>();
- public MavenDependencyComputationData(
- Map modules) {
+ public MavenDependencyComputationData(Map modules) {
this.allModules = modules;
}
+
+ /**
+ * Builds a map of all the modules, keyed against the groupId and artifactId. The values are a list of modules
+ * that match this criteria.
+ *
+ * @return {@link #allModules} keyed by their {@linkplain ModuleName names}.
+ */
+ private Multimap byName() {
+ Multimap map = HashMultimap.create();
+
+ for (ModuleDependency dependency : allModules.keySet()) {
+ map.put(dependency.getName(),dependency);
+ }
+
+ return map;
+ }
}
@Override
@@ -660,5 +686,5 @@ public class MavenModule extends AbstractMavenProject im
}
private static final Logger LOGGER = Logger.getLogger(MavenModule.class.getName());
-
+
}
diff --git a/maven-plugin/src/main/java/hudson/maven/ModuleDependency.java b/maven-plugin/src/main/java/hudson/maven/ModuleDependency.java
index d8b462b1a241dd8f8f33b9c13c64084043381215..2e14c04e94eb2950f0671a641ad1a93d93a17d7c 100644
--- a/maven-plugin/src/main/java/hudson/maven/ModuleDependency.java
+++ b/maven-plugin/src/main/java/hudson/maven/ModuleDependency.java
@@ -23,12 +23,21 @@
*/
package hudson.maven;
+import org.apache.commons.collections.comparators.ReverseComparator;
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.project.MavenProject;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.model.Extension;
import java.io.Serializable;
+import java.util.Collection;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
import hudson.Functions;
@@ -41,6 +50,9 @@ import hudson.Functions;
public final class ModuleDependency implements Serializable {
public final String groupId;
public final String artifactId;
+ /**
+ * Version, possibly a version range.
+ */
public final String version;
/**
@@ -48,6 +60,20 @@ public final class ModuleDependency implements Serializable {
*/
public final boolean plugin;
+ /**
+ * Cached result of {@code VersionRange.createFromVersionSpec(version)}
+ *
+ * @see #getVersionAsRange()
+ */
+ private VersionRange range;
+
+ /**
+ * Cache of the parsed form of {@link #version}
+ *
+ * @see #parseVersion()
+ */
+ private ArtifactVersion parsedVersion;
+
public ModuleDependency(String groupId, String artifactId, String version) {
this(groupId, artifactId, version, false);
}
@@ -135,6 +161,18 @@ public final class ModuleDependency implements Serializable {
return result;
}
+ public VersionRange getVersionAsRange() throws InvalidVersionSpecificationException {
+ if (range==null)
+ range = VersionRange.createFromVersionSpec(version);
+ return range;
+ }
+
+ public ArtifactVersion parseVersion() {
+ if (parsedVersion==null)
+ parsedVersion = new DefaultArtifactVersion(version);
+ return parsedVersion;
+ }
+
/**
* Upon reading from the disk, intern strings.
*/
@@ -163,4 +201,57 @@ public final class ModuleDependency implements Serializable {
public static final String NONE = "-";
private static final long serialVersionUID = 1L;
+
+ /**
+ * Checks whether this ModuleDependency is satisfied by the dependency of the given ModuleDependency.
+ * This caters for versions where the version string defines a version range.
+ *
+ * @param other The dependency to check for.
+ * @return true if contained false otherwise.
+ */
+ public boolean contains(ModuleDependency other) {
+ if (other == null || !getName().equals(other.getName()))
+ return false;
+
+ try {
+ return getVersionAsRange().containsVersion(other.parseVersion());
+ } catch (InvalidVersionSpecificationException ivse) {
+ return false;
+ }
+ }
+
+ /**
+ * Given a list of ModuleDependencies (of the same groupId and artifactId),
+ * picks the {@link ModuleDependency} that satisfies the constraint and has the highest version.
+ *
+ * @param candidates
+ * List that represents specific (non-range) versions.
+ * @return The highest satisfying ModuleDependency or null if none can be found.
+ */
+ public ModuleDependency findHighestFrom(Collection candidates) {
+ //Create a sorted map of the ModuleDependnecies sorted on version (descending order).
+ SortedMap sorted = new TreeMap(new ReverseComparator());
+ for (ModuleDependency candidate : candidates) {
+ sorted.put(candidate.parseVersion(), candidate);
+ }
+
+ //Now find the highest version that satisfies this dependency.
+ for (ModuleDependency e : sorted.values()) {
+ if (contains(e))
+ return e;
+ }
+
+ // non found
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "ModuleDependency{" +
+ "groupId='" + groupId + '\'' +
+ ", artifactId='" + artifactId + '\'' +
+ ", version='" + version + '\'' +
+ ", plugin=" + plugin +
+ '}';
+ }
}
diff --git a/maven-plugin/src/main/java/hudson/maven/ModuleName.java b/maven-plugin/src/main/java/hudson/maven/ModuleName.java
index ff5f6030038820e3cd37e820a7d6a8363ebe06b2..bd03ef4596dc946ebb1dc065d8167d86ac50c250 100644
--- a/maven-plugin/src/main/java/hudson/maven/ModuleName.java
+++ b/maven-plugin/src/main/java/hudson/maven/ModuleName.java
@@ -33,7 +33,7 @@ import org.apache.maven.model.ReportPlugin;
import java.io.Serializable;
/**
- * Version independent name of a Maven project.
+ * Version independent name of a Maven project. GroupID+artifactId.
*
* @author Kohsuke Kawaguchi
* @see ModuleDependency
diff --git a/maven-plugin/src/test/java/hudson/maven/MavenModuleTest.java b/maven-plugin/src/test/java/hudson/maven/MavenModuleTest.java
index e9fcaf0976614392e6a0ac587f2b8dd072b7e3f2..d6b8a27cd536f7795bc8aa1ec93cfd1f85397f9a 100644
--- a/maven-plugin/src/test/java/hudson/maven/MavenModuleTest.java
+++ b/maven-plugin/src/test/java/hudson/maven/MavenModuleTest.java
@@ -10,12 +10,14 @@ import hudson.model.AbstractProject;
import hudson.model.DependencyGraph;
import hudson.model.MockHelper;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import junit.framework.Assert;
import org.apache.maven.model.Build;
+import org.apache.maven.model.Dependency;
import org.apache.maven.model.Plugin;
import org.apache.maven.project.MavenProject;
import org.junit.Before;
@@ -126,4 +128,201 @@ public class MavenModuleTest {
when(parent.getModules()).thenReturn(Collections.singleton(mock));
}
+ /**
+ * This test is a standard project that has a versioned dependency.
+ */
+ @Test
+ public void testSimpleVersion() {
+ TestComponents testComponents = createTestComponents("1.0.1-SNAPSHOT");
+
+ DependencyGraph graph = testComponents.graph;
+ MavenModule appMavenModule = testComponents.applicationMavenModule;
+ MavenModule libMavenModule = testComponents.libraryMavenModule;
+
+ graph.build();
+
+ List appDownstream = graph.getDownstream(appMavenModule);
+ List appUpstream = graph.getUpstream(appMavenModule);
+ List libDownstream = graph.getDownstream(libMavenModule);
+ List libUpstream = graph.getUpstream(libMavenModule);
+
+ Assert.assertEquals(0, appDownstream.size());
+ Assert.assertEquals(1, appUpstream.size());
+ Assert.assertEquals(1, libDownstream.size());
+ Assert.assertEquals(0, libUpstream.size());
+ }
+
+ /**
+ * This tests that a version range declaration in the dependency of a top level project
+ * resolves the up and downstream correctly.
+ */
+ @Test
+ public void testSimpleVersionRange() {
+ TestComponents testComponents = createTestComponents("[1.0.0, )");
+
+ DependencyGraph graph = testComponents.graph;
+ MavenModule appMavenModule = testComponents.applicationMavenModule;
+ MavenModule libMavenModule = testComponents.libraryMavenModule;
+
+ graph.build();
+
+ List appDownstream = graph.getDownstream(appMavenModule);
+ List appUpstream = graph.getUpstream(appMavenModule);
+ List libDownstream = graph.getDownstream(libMavenModule);
+ List libUpstream = graph.getUpstream(libMavenModule);
+
+ Assert.assertEquals(0, appDownstream.size());
+ Assert.assertEquals(1, appUpstream.size());
+ Assert.assertEquals(1, libDownstream.size());
+ Assert.assertEquals(0, libUpstream.size());
+ }
+
+ /**
+ * Test multiple projects with dependencies on differing library versions declared with
+ * multiple version definitions.
+ */
+ @Test
+ public void testMultipleDependencies() {
+
+ MavenProject projectA = createMavenProject("ProjectA", "test", "projectA", "1.0-SNAPSHOT", "jar");
+ Dependency dependencyA = createDependency("test", "library", "[1.0, 2.0)");
+ projectA.getDependencies().add(dependencyA);
+
+ MavenProject projectB = createMavenProject("ProjectB", "test", "projectB", "2.0-SNAPSHOT", "jar");
+ Dependency dependencyB = createDependency("test", "library", "[1.1, 2.1]");
+ projectB.getDependencies().add(dependencyB);
+
+ MavenProject dependX = createMavenProject("DependX-1.1", "test", "library", "1.1.3-SNAPSHOT", "jar");
+ MavenProject dependY = createMavenProject("DependX-1.2", "test", "library", "1.2.1-SNAPSHOT", "jar");
+ MavenProject dependZ = createMavenProject("DependX-2.0", "test", "library", "2.0.1-SNAPSHOT", "jar");
+
+ MavenModuleSet parent = mock(MavenModuleSet.class);
+ when(parent.isAggregatorStyleBuild()).thenReturn(Boolean.FALSE);
+
+ //Now create maven modules for all the projects
+ MavenModule mavenModuleA = mockMavenModule(projectA);
+ MavenModule mavenModuleB = mockMavenModule(projectB);
+ MavenModule mavenModuleX = mockMavenModule(dependX);
+ MavenModule mavenModuleY = mockMavenModule(dependY);
+ MavenModule mavenModuleZ = mockMavenModule(dependZ);
+
+ Collection> allModules = Lists.>newArrayList(mavenModuleA,
+ mavenModuleB, mavenModuleX, mavenModuleY, mavenModuleZ);
+
+ for (AbstractProject, ?> module : allModules) {
+ MavenModule mm = (MavenModule) module;
+ enhanceMavenModuleMock(mm, parent, allModules);
+ }
+
+ DependencyGraph graph = MockHelper.mockDependencyGraph(allModules);
+ doCallRealMethod().when(graph).getDownstream(Matchers.any(AbstractProject.class));
+ doCallRealMethod().when(graph).getUpstream(Matchers.any(AbstractProject.class));
+ doCallRealMethod().when(graph).compare(Matchers.any(), Matchers.any());
+ graph.build();
+
+ List downstreamA = graph.getDownstream(mavenModuleA);
+ List upstreamA = graph.getUpstream(mavenModuleA);
+
+ Assert.assertEquals(0, downstreamA.size());
+ Assert.assertEquals(1, upstreamA.size());
+ Assert.assertSame(dependY.getVersion(), ((MavenModule) upstreamA.get(0)).getVersion());
+
+ List downstreamB = graph.getDownstream(mavenModuleB);
+ List upstreamB = graph.getUpstream(mavenModuleB);
+
+ Assert.assertEquals(0, downstreamB.size());
+ Assert.assertEquals(1, upstreamA.size());
+ Assert.assertSame(dependZ.getVersion(), ((MavenModule) upstreamB.get(0)).getVersion());
+ }
+
+ private TestComponents createTestComponents(String libraryVersion) {
+ MavenProject appProject = createMavenProject("testapp", "test", "application", "1.0-SNAPSHOT", "jar");
+ Dependency dependency = createDependency("test", "library", libraryVersion);
+ appProject.getDependencies().add(dependency);
+
+ MavenModule appMavenModule = mockMavenModule(appProject);
+
+ MavenProject libProject = createLibrary();
+ MavenModule libMavenModule = mockMavenModule(libProject);
+
+ MavenModuleSet parent = mock(MavenModuleSet.class);
+ when(parent.isAggregatorStyleBuild()).thenReturn(Boolean.FALSE);
+ when(appMavenModule.getParent()).thenReturn(parent);
+ when(libMavenModule.getParent()).thenReturn(parent);
+
+ Collection projects = Lists.newArrayList(appMavenModule, libMavenModule);
+ when(parent.getModules()).thenReturn(projects);
+ when(appMavenModule.getAllMavenModules()).thenReturn(projects);
+ when(libMavenModule.getAllMavenModules()).thenReturn(projects);
+
+ DependencyGraph graph = MockHelper.mockDependencyGraph(Lists.>newArrayList(appMavenModule, libMavenModule));
+ doCallRealMethod().when(graph).getDownstream(Matchers.any(AbstractProject.class));
+ doCallRealMethod().when(graph).getUpstream(Matchers.any(AbstractProject.class));
+
+ TestComponents testComponents = new TestComponents();
+ testComponents.graph = graph;
+ testComponents.applicationMavenModule = appMavenModule;
+ testComponents.libraryMavenModule = libMavenModule;
+
+ return testComponents;
+ }
+
+ private static void enhanceMavenModuleMock(MavenModule module,
+ MavenModuleSet parent,
+ Collection allProjects) {
+ when(module.getParent()).thenReturn(parent);
+ when(module.getAllMavenModules()).thenReturn(allProjects);
+ }
+
+ private static MavenModule mockMavenModule(MavenProject project) {
+ MavenModule mavenModule = mock(MavenModule.class);
+ when(mavenModule.getName()).thenReturn(project.getName());
+ basicMocking(mavenModule);
+ mavenModule.doSetName(project.getGroupId() + '$' + project.getArtifactId());
+
+ PomInfo pomInfo = new PomInfo(project, null, "relPath");
+ mavenModule.reconfigure(pomInfo);
+
+ return mavenModule;
+ }
+
+ private static MavenProject createMavenProject(String name,
+ String groupId,
+ String artifactId,
+ String version,
+ String packaging) {
+ MavenProject proj = new MavenProject();
+ proj.setName(name);
+ proj.setGroupId(groupId);
+ proj.setArtifactId(artifactId);
+ proj.setVersion(version);
+ proj.setPackaging(packaging);
+
+ return proj;
+ }
+
+ private static Dependency createDependency(String groupId, String artifactId, String version) {
+ Dependency dependency = new Dependency();
+ dependency.setGroupId(groupId);
+ dependency.setArtifactId(artifactId);
+ dependency.setVersion(version);
+ return dependency;
+ }
+
+ private static MavenProject createLibrary() {
+ MavenProject proj = createMavenProject("testlib", "test", "library", "1.0.1-SNAPSHOT", "jar");
+
+ Dependency dependency = new Dependency();
+ dependency.setArtifactId("log4j");
+ dependency.setGroupId("log4j");
+ dependency.setVersion("1.6.15");
+ proj.getDependencies().add(dependency);
+ return proj;
+ }
+
+ private static class TestComponents {
+ public DependencyGraph graph;
+ public MavenModule applicationMavenModule;
+ public MavenModule libraryMavenModule;
+ }
}