Skip to content

Commit 4452363

Browse files
cstamasgnodet
andauthored
Make rootDirectory mandatory (#1787)
Co-authored-by: Guillaume Nodet <[email protected]>
1 parent 6c8b808 commit 4452363

File tree

23 files changed

+188
-100
lines changed

23 files changed

+188
-100
lines changed

api/maven-api-core/src/main/java/org/apache/maven/api/Session.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,6 @@ public interface Session {
158158
* Gets the root directory of the session, which is the root directory for the top directory project.
159159
*
160160
* @return the root directory, never {@code null}
161-
* @throws IllegalStateException if the root directory could not be found
162161
* @see #getTopDirectory()
163162
* @see Project#getRootDirectory()
164163
* @see Project#isRootProject()
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.api.services.model;
20+
21+
import java.nio.file.Path;
22+
23+
import org.apache.maven.api.Service;
24+
25+
/**
26+
* Interface used to detect is a given directory "root directory".
27+
*/
28+
public interface RootDetector extends Service {
29+
boolean isRootDirectory(Path dir);
30+
}

maven-api-impl/src/main/java/org/apache/maven/api/services/model/RootLocator.java

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@
2222

2323
import org.apache.maven.api.Service;
2424
import org.apache.maven.api.annotations.Nonnull;
25-
import org.apache.maven.api.annotations.Nullable;
2625

2726
/**
2827
* Interface used to locate the root directory for a given project.
2928
*
30-
* The root locator is usually looked up from the plexus container.
29+
* The root locator is usually looked up from the DI container.
3130
* One notable exception is the computation of the early {@code session.rootDirectory}
3231
* property which happens very early. The implementation used in this case
3332
* will be discovered using the JDK service mechanism.
@@ -42,27 +41,10 @@ public interface RootLocator extends Service {
4241
+ " attribute on the root project's model to identify it.";
4342

4443
@Nonnull
45-
default Path findMandatoryRoot(@Nullable Path basedir) {
46-
Path rootDirectory = findRoot(basedir);
47-
if (rootDirectory == null) {
48-
throw new IllegalStateException(getNoRootMessage());
49-
}
50-
return rootDirectory;
51-
}
52-
53-
@Nullable
54-
default Path findRoot(@Nullable Path basedir) {
55-
Path rootDirectory = basedir;
56-
while (rootDirectory != null && !isRootDirectory(rootDirectory)) {
57-
rootDirectory = rootDirectory.getParent();
58-
}
59-
return rootDirectory;
60-
}
44+
Path findMandatoryRoot(@Nonnull Path basedir);
6145

6246
@Nonnull
6347
default String getNoRootMessage() {
6448
return UNABLE_TO_FIND_ROOT_PROJECT_MESSAGE;
6549
}
66-
67-
boolean isRootDirectory(Path dir);
6850
}

maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@
106106
import org.apache.maven.api.services.model.ProfileActivationContext;
107107
import org.apache.maven.api.services.model.ProfileInjector;
108108
import org.apache.maven.api.services.model.ProfileSelector;
109-
import org.apache.maven.api.services.model.RootLocator;
110109
import org.apache.maven.api.services.xml.XmlReaderException;
111110
import org.apache.maven.api.services.xml.XmlReaderRequest;
112111
import org.apache.maven.api.spi.ModelParserException;
@@ -633,12 +632,7 @@ private void buildBuildPom() throws ModelBuilderException {
633632
top = top.toAbsolutePath().normalize();
634633

635634
// Obtain the root directory, resolving it if necessary
636-
Path rootDirectory;
637-
try {
638-
rootDirectory = session.getRootDirectory();
639-
} catch (IllegalStateException e) {
640-
rootDirectory = session.getService(RootLocator.class).findRoot(top);
641-
}
635+
Path rootDirectory = session.getRootDirectory();
642636

643637
// Locate and normalize the root POM if it exists, fallback to top otherwise
644638
Path root = modelProcessor.locateExistingPom(rootDirectory);
@@ -1177,19 +1171,11 @@ Model readFileModel() throws ModelBuilderException {
11771171
Model doReadFileModel() throws ModelBuilderException {
11781172
ModelSource modelSource = request.getSource();
11791173
Model model;
1180-
Path rootDirectory;
1174+
Path rootDirectory = request.getSession().getRootDirectory();
11811175
setSource(modelSource.getLocation());
11821176
logger.debug("Reading file model from " + modelSource.getLocation());
11831177
try {
11841178
boolean strict = request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM;
1185-
try {
1186-
rootDirectory = request.getSession().getRootDirectory();
1187-
} catch (IllegalStateException ignore) {
1188-
rootDirectory = modelSource.getPath();
1189-
while (rootDirectory != null && !Files.isDirectory(rootDirectory)) {
1190-
rootDirectory = rootDirectory.getParent();
1191-
}
1192-
}
11931179
try (InputStream is = modelSource.openStream()) {
11941180
model = modelProcessor.read(XmlReaderRequest.builder()
11951181
.strict(strict)
@@ -1592,12 +1578,7 @@ private Model doLoadDependencyManagement(
15921578
return null;
15931579
}
15941580

1595-
Path rootDirectory;
1596-
try {
1597-
rootDirectory = request.getSession().getRootDirectory();
1598-
} catch (IllegalStateException e) {
1599-
rootDirectory = null;
1600-
}
1581+
Path rootDirectory = request.getSession().getRootDirectory();
16011582
if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM && rootDirectory != null) {
16021583
Path sourcePath = importSource.getPath();
16031584
if (sourcePath != null && sourcePath.startsWith(rootDirectory)) {
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.internal.impl.model.rootlocator;
20+
21+
import java.nio.file.Path;
22+
import java.nio.file.Paths;
23+
import java.util.List;
24+
import java.util.Objects;
25+
import java.util.Optional;
26+
import java.util.ServiceLoader;
27+
28+
import org.apache.maven.api.annotations.Nonnull;
29+
import org.apache.maven.api.di.Named;
30+
import org.apache.maven.api.services.model.RootDetector;
31+
import org.apache.maven.api.services.model.RootLocator;
32+
import org.slf4j.Logger;
33+
import org.slf4j.LoggerFactory;
34+
35+
import static java.util.Objects.requireNonNull;
36+
37+
@Named
38+
public class DefaultRootLocator implements RootLocator {
39+
private final Logger logger = LoggerFactory.getLogger(getClass());
40+
41+
private final List<RootDetector> rootDetectors;
42+
43+
public DefaultRootLocator() {
44+
this.rootDetectors = ServiceLoader.load(RootDetector.class).stream()
45+
.map(ServiceLoader.Provider::get)
46+
.toList();
47+
}
48+
49+
@Nonnull
50+
public Path findMandatoryRoot(@Nonnull Path basedir) {
51+
requireNonNull(basedir, "basedir is null");
52+
Path rootDirectory = basedir;
53+
while (rootDirectory != null && !isRootDirectory(rootDirectory)) {
54+
rootDirectory = rootDirectory.getParent();
55+
}
56+
Optional<Path> rdf = getMultiModuleProjectDirectory();
57+
if (rootDirectory == null) {
58+
logger.warn(getNoRootMessage());
59+
rootDirectory = rdf.orElseGet(() -> Paths.get("").toAbsolutePath());
60+
} else {
61+
if (rdf.isPresent() && !Objects.equals(rootDirectory, rdf.get())) {
62+
logger.warn("Project root directory and multiModuleProjectDirectory are not aligned");
63+
}
64+
}
65+
return rootDirectory;
66+
}
67+
68+
protected boolean isRootDirectory(Path dir) {
69+
requireNonNull(dir, "dir is null");
70+
for (RootDetector rootDetector : rootDetectors) {
71+
if (rootDetector.isRootDirectory(dir)) {
72+
return true;
73+
}
74+
}
75+
return false;
76+
}
77+
78+
protected Optional<Path> getMultiModuleProjectDirectory() {
79+
String mmpd = System.getProperty("maven.multiModuleProjectDirectory");
80+
if (mmpd != null) {
81+
return Optional.of(Paths.get(mmpd));
82+
}
83+
return Optional.empty();
84+
}
85+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.internal.impl.model.rootlocator;
20+
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
24+
import org.apache.maven.api.di.Named;
25+
import org.apache.maven.api.services.model.RootDetector;
26+
27+
@Named
28+
public class DotMvnRootDetector implements RootDetector {
29+
@Override
30+
public boolean isRootDirectory(Path dir) {
31+
return Files.isDirectory(dir.resolve(".mvn"));
32+
}
33+
}

maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultRootLocator.java renamed to maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/rootlocator/PomXmlRootDetector.java

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
package org.apache.maven.internal.impl.model;
19+
package org.apache.maven.internal.impl.model.rootlocator;
2020

2121
import javax.xml.stream.XMLInputFactory;
2222
import javax.xml.stream.XMLStreamException;
@@ -27,28 +27,13 @@
2727
import java.nio.file.Files;
2828
import java.nio.file.Path;
2929

30-
import org.apache.maven.api.annotations.Nullable;
3130
import org.apache.maven.api.di.Named;
32-
import org.apache.maven.api.services.model.RootLocator;
31+
import org.apache.maven.api.services.model.RootDetector;
3332

3433
@Named
35-
public class DefaultRootLocator implements RootLocator {
36-
37-
@Override
38-
@Nullable
39-
public Path findRoot(Path basedir) {
40-
Path rootDirectory = basedir;
41-
while (rootDirectory != null && !isRootDirectory(rootDirectory)) {
42-
rootDirectory = rootDirectory.getParent();
43-
}
44-
return rootDirectory;
45-
}
46-
34+
public class PomXmlRootDetector implements RootDetector {
4735
@Override
4836
public boolean isRootDirectory(Path dir) {
49-
if (Files.isDirectory(dir.resolve(".mvn"))) {
50-
return true;
51-
}
5237
// we're too early to use the modelProcessor ...
5338
Path pom = dir.resolve("pom.xml");
5439
try (InputStream is = Files.newInputStream(pom)) {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
org.apache.maven.internal.impl.model.rootlocator.DotMvnRootDetector
2+
org.apache.maven.internal.impl.model.rootlocator.PomXmlRootDetector
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.apache.maven.internal.impl.model.rootlocator.DefaultRootLocator

maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/ApiRunner.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.apache.maven.api.services.RepositoryFactory;
5353
import org.apache.maven.api.services.SettingsBuilder;
5454
import org.apache.maven.api.services.TypeRegistry;
55+
import org.apache.maven.api.services.model.RootLocator;
5556
import org.apache.maven.api.settings.Settings;
5657
import org.apache.maven.api.spi.TypeProvider;
5758
import org.apache.maven.di.Injector;
@@ -153,12 +154,12 @@ public Instant getStartTime() {
153154

154155
@Override
155156
public Path getTopDirectory() {
156-
return null;
157+
return Paths.get("");
157158
}
158159

159160
@Override
160161
public Path getRootDirectory() {
161-
throw new IllegalStateException();
162+
return getService(RootLocator.class).findMandatoryRoot(getTopDirectory());
162163
}
163164

164165
@Override

0 commit comments

Comments
 (0)