mirror of https://github.com/apache/maven.git
[MNG-8084] New model builder and resolver provider
This commit is contained in:
parent
9be08ccef8
commit
d075fe7e85
|
@ -18,6 +18,8 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.maven.api;
|
package org.apache.maven.api;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.maven.api.annotations.Experimental;
|
import org.apache.maven.api.annotations.Experimental;
|
||||||
import org.apache.maven.api.annotations.Immutable;
|
import org.apache.maven.api.annotations.Immutable;
|
||||||
import org.apache.maven.api.annotations.Nonnull;
|
import org.apache.maven.api.annotations.Nonnull;
|
||||||
|
@ -55,9 +57,9 @@ public interface Packaging extends ExtensibleEnum {
|
||||||
Type type();
|
Type type();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the binding to use specifically for this packaging.
|
* Returns the binding to use specifically for this packaging keyed by lifecycle id.
|
||||||
* This will be merged to the default packaging definition.
|
* This will be used instead of the default packaging definition.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
PluginContainer plugins();
|
Map<String, PluginContainer> plugins();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.maven.api.Service;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
|
||||||
|
public interface ModelBuilder extends Service {
|
||||||
|
|
||||||
|
List<String> VALID_MODEL_VERSIONS = List.of("4.0.0", "4.1.0");
|
||||||
|
|
||||||
|
ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilderException;
|
||||||
|
|
||||||
|
ModelTransformerContextBuilder newTransformerContextBuilder();
|
||||||
|
|
||||||
|
Model buildRawModel(ModelBuilderRequest request);
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.maven.api.annotations.Experimental;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Exception class throw by the {@link ProjectBuilder} service.
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
@Experimental
|
||||||
|
public class ModelBuilderException extends MavenException {
|
||||||
|
|
||||||
|
private final ModelBuilderResult result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new exception from the specified interim result and its associated problems.
|
||||||
|
*
|
||||||
|
* @param result The interim result, may be {@code null}.
|
||||||
|
*/
|
||||||
|
public ModelBuilderException(ModelBuilderResult result) {
|
||||||
|
super(result.toString());
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the interim result of the model building up to the point where it failed.
|
||||||
|
*
|
||||||
|
* @return The interim model building result or {@code null} if not available.
|
||||||
|
*/
|
||||||
|
public ModelBuilderResult getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the identifier of the POM whose effective model could not be built. The general format of the identifier is
|
||||||
|
* {@code <groupId>:<artifactId>:<version>} but some of these coordinates may still be unknown at the point the
|
||||||
|
* exception is thrown so this information is merely meant to assist the user.
|
||||||
|
*
|
||||||
|
* @return The identifier of the POM or an empty string if not known, never {@code null}.
|
||||||
|
*/
|
||||||
|
public String getModelId() {
|
||||||
|
if (result == null || result.getModelIds().isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return result.getModelIds().get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the problems that caused this exception.
|
||||||
|
*
|
||||||
|
* @return The problems that caused this exception, never {@code null}.
|
||||||
|
*/
|
||||||
|
public List<ModelProblem> getProblems() {
|
||||||
|
if (result == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(result.getProblems());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,454 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.maven.api.Session;
|
||||||
|
import org.apache.maven.api.annotations.Experimental;
|
||||||
|
import org.apache.maven.api.annotations.Immutable;
|
||||||
|
import org.apache.maven.api.annotations.Nonnull;
|
||||||
|
import org.apache.maven.api.annotations.NotThreadSafe;
|
||||||
|
import org.apache.maven.api.annotations.Nullable;
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
|
||||||
|
import static org.apache.maven.api.services.BaseRequest.nonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request used to build a {@link org.apache.maven.api.Project} using
|
||||||
|
* the {@link ProjectBuilder} service.
|
||||||
|
*
|
||||||
|
* TODO: add validationLevel, activeProfileIds, inactiveProfileIds, resolveDependencies
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
@Experimental
|
||||||
|
@Immutable
|
||||||
|
public interface ModelBuilderRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denotes minimal validation of POMs. This validation level is meant for processing of POMs from repositories
|
||||||
|
* during metadata retrieval.
|
||||||
|
*/
|
||||||
|
int VALIDATION_LEVEL_MINIMAL = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denotes validation as performed by Maven 2.0. This validation level is meant as a compatibility mode to allow
|
||||||
|
* users to migrate their projects.
|
||||||
|
*/
|
||||||
|
int VALIDATION_LEVEL_MAVEN_2_0 = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denotes validation as performed by Maven 3.0. This validation level is meant for existing projects.
|
||||||
|
*/
|
||||||
|
int VALIDATION_LEVEL_MAVEN_3_0 = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denotes validation as performed by Maven 3.1. This validation level is meant for existing projects.
|
||||||
|
*/
|
||||||
|
int VALIDATION_LEVEL_MAVEN_3_1 = 31;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denotes validation as performed by Maven 4.0. This validation level is meant for new projects.
|
||||||
|
*/
|
||||||
|
int VALIDATION_LEVEL_MAVEN_4_0 = 40;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denotes strict validation as recommended by the current Maven version.
|
||||||
|
*/
|
||||||
|
int VALIDATION_LEVEL_STRICT = VALIDATION_LEVEL_MAVEN_4_0;
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
Session getSession();
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
ModelSource getSource();
|
||||||
|
|
||||||
|
int getValidationLevel();
|
||||||
|
|
||||||
|
boolean isTwoPhaseBuilding();
|
||||||
|
|
||||||
|
boolean isLocationTracking();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the model to be built is a local project or a dependency.
|
||||||
|
* In case the project is loaded externally from a remote repository (as a dependency or
|
||||||
|
* even as an external parent), the POM will be parsed in a lenient way. Local POMs
|
||||||
|
* are parsed more strictly.
|
||||||
|
*/
|
||||||
|
boolean isProjectBuild();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether plugin processing should take place for the built model.
|
||||||
|
* This involves merging plugins specified by the {@link org.apache.maven.api.Packaging},
|
||||||
|
* configuration expansion (merging configuration defined globally for a given plugin
|
||||||
|
* using {@link org.apache.maven.api.model.Plugin#getConfiguration()}
|
||||||
|
* into the configuration for each {@link org.apache.maven.api.model.PluginExecution}.
|
||||||
|
*/
|
||||||
|
boolean isProcessPlugins();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines external profiles that may be activated for the given model.
|
||||||
|
* Those are external profiles usually defined in {@link org.apache.maven.api.settings.Settings#getProfiles()}.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Collection<Profile> getProfiles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of profile ids that have been explicitly activated by the user.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
List<String> getActiveProfileIds();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of profile ids that have been explicitly deactivated by the user.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
List<String> getInactiveProfileIds();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a map of system properties.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Map<String, String> getSystemProperties();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a map of user properties.
|
||||||
|
* User properties
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Map<String, String> getUserProperties();
|
||||||
|
|
||||||
|
ModelResolver getModelResolver();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
Object getListener();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
ModelBuilderResult getInterimResult();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
ModelTransformerContextBuilder getTransformerContextBuilder();
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
static ModelBuilderRequest build(@Nonnull ModelBuilderRequest request, @Nonnull ModelSource source) {
|
||||||
|
return builder(nonNull(request, "request cannot be null"))
|
||||||
|
.source(nonNull(source, "source cannot be null"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
static ModelBuilderRequest build(@Nonnull Session session, @Nonnull ModelSource source) {
|
||||||
|
return builder()
|
||||||
|
.session(nonNull(session, "session cannot be null"))
|
||||||
|
.source(nonNull(source, "source cannot be null"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
static ModelBuilderRequest build(@Nonnull Session session, @Nonnull Path path) {
|
||||||
|
return builder()
|
||||||
|
.session(nonNull(session, "session cannot be null"))
|
||||||
|
.source(ModelSource.fromPath(path))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
static ModelBuilderRequestBuilder builder() {
|
||||||
|
return new ModelBuilderRequestBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
static ModelBuilderRequestBuilder builder(ModelBuilderRequest request) {
|
||||||
|
return new ModelBuilderRequestBuilder(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotThreadSafe
|
||||||
|
class ModelBuilderRequestBuilder {
|
||||||
|
Session session;
|
||||||
|
int validationLevel;
|
||||||
|
boolean locationTracking;
|
||||||
|
boolean twoPhaseBuilding;
|
||||||
|
ModelSource source;
|
||||||
|
boolean projectBuild;
|
||||||
|
boolean processPlugins = true;
|
||||||
|
Collection<Profile> profiles;
|
||||||
|
List<String> activeProfileIds;
|
||||||
|
List<String> inactiveProfileIds;
|
||||||
|
Map<String, String> systemProperties;
|
||||||
|
Map<String, String> userProperties;
|
||||||
|
ModelResolver modelResolver;
|
||||||
|
Object listener;
|
||||||
|
ModelBuilderResult interimResult;
|
||||||
|
ModelTransformerContextBuilder transformerContextBuilder;
|
||||||
|
|
||||||
|
ModelBuilderRequestBuilder() {}
|
||||||
|
|
||||||
|
ModelBuilderRequestBuilder(ModelBuilderRequest request) {
|
||||||
|
this.session = request.getSession();
|
||||||
|
this.validationLevel = request.getValidationLevel();
|
||||||
|
this.locationTracking = request.isLocationTracking();
|
||||||
|
this.twoPhaseBuilding = request.isTwoPhaseBuilding();
|
||||||
|
this.source = request.getSource();
|
||||||
|
this.projectBuild = request.isProjectBuild();
|
||||||
|
this.processPlugins = request.isProcessPlugins();
|
||||||
|
this.profiles = request.getProfiles();
|
||||||
|
this.activeProfileIds = request.getActiveProfileIds();
|
||||||
|
this.inactiveProfileIds = request.getInactiveProfileIds();
|
||||||
|
this.systemProperties = request.getSystemProperties();
|
||||||
|
this.userProperties = request.getUserProperties();
|
||||||
|
this.modelResolver = request.getModelResolver();
|
||||||
|
this.listener = request.getListener();
|
||||||
|
this.interimResult = request.getInterimResult();
|
||||||
|
this.transformerContextBuilder = request.getTransformerContextBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder session(Session session) {
|
||||||
|
this.session = session;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder validationLevel(int validationLevel) {
|
||||||
|
this.validationLevel = validationLevel;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder twoPhaseBuilding(boolean twoPhaseBuilding) {
|
||||||
|
this.twoPhaseBuilding = twoPhaseBuilding;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder locationTracking(boolean locationTracking) {
|
||||||
|
this.locationTracking = locationTracking;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder source(ModelSource source) {
|
||||||
|
this.source = source;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder projectBuild(boolean projectBuild) {
|
||||||
|
this.projectBuild = projectBuild;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder processPlugins(boolean processPlugins) {
|
||||||
|
this.processPlugins = processPlugins;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder profiles(List<Profile> profiles) {
|
||||||
|
this.profiles = profiles;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder activeProfileIds(List<String> activeProfileIds) {
|
||||||
|
this.activeProfileIds = activeProfileIds;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder inactiveProfileIds(List<String> inactiveProfileIds) {
|
||||||
|
this.inactiveProfileIds = inactiveProfileIds;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder systemProperties(Map<String, String> systemProperties) {
|
||||||
|
this.systemProperties = systemProperties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder userProperties(Map<String, String> userProperties) {
|
||||||
|
this.userProperties = userProperties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder modelResolver(ModelResolver modelResolver) {
|
||||||
|
this.modelResolver = modelResolver;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder listener(Object listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder interimResult(ModelBuilderResult interimResult) {
|
||||||
|
this.interimResult = interimResult;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequestBuilder transformerContextBuilder(
|
||||||
|
ModelTransformerContextBuilder transformerContextBuilder) {
|
||||||
|
this.transformerContextBuilder = transformerContextBuilder;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderRequest build() {
|
||||||
|
return new DefaultModelBuilderRequest(
|
||||||
|
session,
|
||||||
|
validationLevel,
|
||||||
|
locationTracking,
|
||||||
|
twoPhaseBuilding,
|
||||||
|
source,
|
||||||
|
projectBuild,
|
||||||
|
processPlugins,
|
||||||
|
profiles,
|
||||||
|
activeProfileIds,
|
||||||
|
inactiveProfileIds,
|
||||||
|
systemProperties,
|
||||||
|
userProperties,
|
||||||
|
modelResolver,
|
||||||
|
listener,
|
||||||
|
interimResult,
|
||||||
|
transformerContextBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DefaultModelBuilderRequest extends BaseRequest implements ModelBuilderRequest {
|
||||||
|
private final int validationLevel;
|
||||||
|
private final boolean locationTracking;
|
||||||
|
private final boolean twoPhaseBuilding;
|
||||||
|
private final ModelSource source;
|
||||||
|
private final boolean projectBuild;
|
||||||
|
private final boolean processPlugins;
|
||||||
|
private final Collection<Profile> profiles;
|
||||||
|
private final List<String> activeProfileIds;
|
||||||
|
private final List<String> inactiveProfileIds;
|
||||||
|
private final Map<String, String> systemProperties;
|
||||||
|
private final Map<String, String> userProperties;
|
||||||
|
private final ModelResolver modelResolver;
|
||||||
|
private final Object listener;
|
||||||
|
private final ModelBuilderResult interimResult;
|
||||||
|
private final ModelTransformerContextBuilder transformerContextBuilder;
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:ParameterNumber")
|
||||||
|
DefaultModelBuilderRequest(
|
||||||
|
@Nonnull Session session,
|
||||||
|
int validationLevel,
|
||||||
|
boolean locationTracking,
|
||||||
|
boolean twoPhaseBuilding,
|
||||||
|
@Nonnull ModelSource source,
|
||||||
|
boolean projectBuild,
|
||||||
|
boolean processPlugins,
|
||||||
|
Collection<Profile> profiles,
|
||||||
|
List<String> activeProfileIds,
|
||||||
|
List<String> inactiveProfileIds,
|
||||||
|
Map<String, String> systemProperties,
|
||||||
|
Map<String, String> userProperties,
|
||||||
|
ModelResolver modelResolver,
|
||||||
|
Object listener,
|
||||||
|
ModelBuilderResult interimResult,
|
||||||
|
ModelTransformerContextBuilder transformerContextBuilder) {
|
||||||
|
super(session);
|
||||||
|
this.validationLevel = validationLevel;
|
||||||
|
this.locationTracking = locationTracking;
|
||||||
|
this.twoPhaseBuilding = twoPhaseBuilding;
|
||||||
|
this.source = source;
|
||||||
|
this.projectBuild = projectBuild;
|
||||||
|
this.processPlugins = processPlugins;
|
||||||
|
this.profiles = profiles != null ? List.copyOf(profiles) : List.of();
|
||||||
|
this.activeProfileIds = activeProfileIds != null ? List.copyOf(activeProfileIds) : List.of();
|
||||||
|
this.inactiveProfileIds = inactiveProfileIds != null ? List.copyOf(inactiveProfileIds) : List.of();
|
||||||
|
this.systemProperties =
|
||||||
|
systemProperties != null ? Map.copyOf(systemProperties) : session.getSystemProperties();
|
||||||
|
this.userProperties = userProperties != null ? Map.copyOf(userProperties) : session.getUserProperties();
|
||||||
|
this.modelResolver = modelResolver;
|
||||||
|
this.listener = listener;
|
||||||
|
this.interimResult = interimResult;
|
||||||
|
this.transformerContextBuilder = transformerContextBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getValidationLevel() {
|
||||||
|
return validationLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTwoPhaseBuilding() {
|
||||||
|
return twoPhaseBuilding;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLocationTracking() {
|
||||||
|
return locationTracking;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public ModelSource getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isProjectBuild() {
|
||||||
|
return projectBuild;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isProcessPlugins() {
|
||||||
|
return processPlugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Profile> getProfiles() {
|
||||||
|
return profiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getActiveProfileIds() {
|
||||||
|
return activeProfileIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getInactiveProfileIds() {
|
||||||
|
return inactiveProfileIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getSystemProperties() {
|
||||||
|
return systemProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getUserProperties() {
|
||||||
|
return userProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelResolver getModelResolver() {
|
||||||
|
return modelResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getListener() {
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelBuilderResult getInterimResult() {
|
||||||
|
return interimResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelTransformerContextBuilder getTransformerContextBuilder() {
|
||||||
|
return transformerContextBuilder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.apache.maven.api.annotations.Experimental;
|
||||||
|
import org.apache.maven.api.annotations.Nonnull;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of a project build call.
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
@Experimental
|
||||||
|
public interface ModelBuilderResult {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the sequence of model identifiers that denote the lineage of models from which the effective model was
|
||||||
|
* constructed. Model identifiers have the form {@code <groupId>:<artifactId>:<version>}. The first identifier from
|
||||||
|
* the list denotes the model on which the model builder was originally invoked. The last identifier will always be
|
||||||
|
* an empty string that by definition denotes the super POM.
|
||||||
|
*
|
||||||
|
* @return The model identifiers from the lineage of models, never {@code null}.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
List<String> getModelIds();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the file model.
|
||||||
|
*
|
||||||
|
* @return the file model, never {@code null}.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Model getFileModel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file model + profile injection.
|
||||||
|
*
|
||||||
|
* @return the activated file model, never {@code null}.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Model getActivatedFileModel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the file model + build pom transformation, without inheritance nor interpolation.
|
||||||
|
*
|
||||||
|
* @return The raw model, never {@code null}.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Model getRawModel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the assembled model with inheritance, interpolation and profile injection.
|
||||||
|
*
|
||||||
|
* @return The assembled model, never {@code null}.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Model getEffectiveModel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the specified raw model as it was read from a model source. Apart from basic validation, a raw model has not
|
||||||
|
* undergone any updates by the model builder, e.g. reflects neither inheritance nor interpolation. The model
|
||||||
|
* identifier should be from the collection obtained by {@link #getModelIds()}. As a special case, an empty string
|
||||||
|
* can be used as the identifier for the super POM.
|
||||||
|
*
|
||||||
|
* @param modelId The identifier of the desired raw model, must not be {@code null}.
|
||||||
|
* @return The raw model or {@code null} if the specified model id does not refer to a known model.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Optional<Model> getRawModel(@Nonnull String modelId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the profiles from the specified model that were active during model building. The model identifier should be
|
||||||
|
* from the collection obtained by {@link #getModelIds()}. As a special case, an empty string can be used as the
|
||||||
|
* identifier for the super POM.
|
||||||
|
*
|
||||||
|
* @param modelId The identifier of the model whose active profiles should be retrieved, must not be {@code null}.
|
||||||
|
* @return The active profiles of the model or an empty list if the specified model id does
|
||||||
|
* not refer to a known model or has no active profiles.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
List<Profile> getActivePomProfiles(@Nonnull String modelId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the external profiles that were active during model building. External profiles are those that were
|
||||||
|
* contributed by {@link ModelBuilderRequest#getProfiles()}.
|
||||||
|
*
|
||||||
|
* @return The active external profiles or an empty list if none, never {@code null}.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
List<Profile> getActiveExternalProfiles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the problems that were encountered during the project building.
|
||||||
|
*
|
||||||
|
* @return the problems that were encountered during the project building, can be empty but never {@code null}
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
List<ModelProblem> getProblems();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a human readable representation of these errors.
|
||||||
|
*/
|
||||||
|
String toString();
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a problem that was encountered during model building. A problem can either be an exception that was thrown
|
||||||
|
* or a simple string message. In addition, a problem carries a hint about its source, e.g. the POM file that exhibits
|
||||||
|
* the problem.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ModelProblem extends BuilderProblem {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version
|
||||||
|
*/
|
||||||
|
enum Version {
|
||||||
|
// based on ModeBuildingResult.validationLevel
|
||||||
|
BASE,
|
||||||
|
V20,
|
||||||
|
V30,
|
||||||
|
V31,
|
||||||
|
V40
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the identifier of the model from which the problem originated. While the general form of this identifier is
|
||||||
|
* <code>groupId:artifactId:version</code> the returned identifier need not be complete. The identifier is derived
|
||||||
|
* from the information that is available at the point the problem occurs and as such merely serves as a best effort
|
||||||
|
* to provide information to the user to track the problem back to its origin.
|
||||||
|
*
|
||||||
|
* @return The identifier of the model from which the problem originated or an empty string if unknown, never
|
||||||
|
* {@code null}.
|
||||||
|
*/
|
||||||
|
String getModelId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the applicable maven version/validation level of this problem
|
||||||
|
* @return The version, never {@code null}.
|
||||||
|
*/
|
||||||
|
Version getVersion();
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.InputLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects problems that are encountered during model building. The primary purpose of this component is to account for
|
||||||
|
* the fact that the problem reporter has/should not have information about the calling context and hence cannot provide
|
||||||
|
* an expressive source hint for the model problem. Instead, the source hint is configured by the model builder before
|
||||||
|
* it delegates to other components that potentially encounter problems. Then, the problem reporter can focus on
|
||||||
|
* providing a simple error message, leaving the donkey work of creating a nice model problem to this component.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ModelProblemCollector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The collected problems.
|
||||||
|
* @return a list of model problems encountered, never {@code null}
|
||||||
|
*/
|
||||||
|
List<ModelProblem> getProblems();
|
||||||
|
|
||||||
|
boolean hasErrors();
|
||||||
|
|
||||||
|
boolean hasFatalErrors();
|
||||||
|
|
||||||
|
default void add(BuilderProblem.Severity severity, ModelProblem.Version version, String message) {
|
||||||
|
add(severity, version, message, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void add(
|
||||||
|
BuilderProblem.Severity severity, ModelProblem.Version version, String message, InputLocation location) {
|
||||||
|
add(severity, version, message, location, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void add(
|
||||||
|
BuilderProblem.Severity severity, ModelProblem.Version version, String message, Exception exception) {
|
||||||
|
add(severity, version, message, null, exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(
|
||||||
|
BuilderProblem.Severity severity,
|
||||||
|
ModelProblem.Version version,
|
||||||
|
String message,
|
||||||
|
InputLocation location,
|
||||||
|
Exception exception);
|
||||||
|
|
||||||
|
void add(ModelProblem problem);
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import org.apache.maven.api.Service;
|
||||||
|
import org.apache.maven.api.Session;
|
||||||
|
import org.apache.maven.api.annotations.Nonnull;
|
||||||
|
import org.apache.maven.api.model.Dependency;
|
||||||
|
import org.apache.maven.api.model.Parent;
|
||||||
|
import org.apache.maven.api.model.Repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a POM from its coordinates. During the build process, the
|
||||||
|
* {@link org.apache.maven.api.services.ModelBuilder} will add any relevant repositories to the model resolver. In
|
||||||
|
* other words, the model resolver is stateful and should not be reused across multiple model building requests.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ModelResolver extends Service {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to resolve the POM for the specified coordinates.
|
||||||
|
*
|
||||||
|
* @param groupId The group identifier of the POM, must not be {@code null}.
|
||||||
|
* @param artifactId The artifact identifier of the POM, must not be {@code null}.
|
||||||
|
* @param version The version of the POM, must not be {@code null}.
|
||||||
|
* @return The source of the requested POM, never {@code null}.
|
||||||
|
* @throws ModelResolverException If the POM could not be resolved from any configured repository.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
ModelSource resolveModel(
|
||||||
|
@Nonnull Session session, @Nonnull String groupId, @Nonnull String artifactId, @Nonnull String version)
|
||||||
|
throws ModelResolverException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to resolve the POM for the specified parent coordinates possibly updating {@code parent}.
|
||||||
|
* <p>
|
||||||
|
* Unlike the {@link #resolveModel(Session, String, String, String)} method, this method
|
||||||
|
* supports version ranges and updates the given {@code parent} instance to match the returned {@code ModelSource}.
|
||||||
|
* If {@code parent} declares a version range, the version corresponding to the returned {@code ModelSource} will
|
||||||
|
* be set on the given {@code parent}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param parent The parent coordinates to resolve, must not be {@code null}.
|
||||||
|
*
|
||||||
|
* @return The source of the requested POM, never {@code null}.
|
||||||
|
*
|
||||||
|
* @throws ModelResolverException If the POM could not be resolved from any configured repository.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
ModelSource resolveModel(
|
||||||
|
@Nonnull Session session, @Nonnull Parent parent, @Nonnull AtomicReference<Parent> modified)
|
||||||
|
throws ModelResolverException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to resolve the POM for the specified dependency coordinates possibly updating {@code dependency}.
|
||||||
|
* <p>
|
||||||
|
* Unlike the {@link #resolveModel(Session, String, String, String)} method, this method
|
||||||
|
* supports version ranges and updates the given {@code dependency} instance to match the returned
|
||||||
|
* {@code ModelSource}. If {@code dependency} declares a version range, the version corresponding to the returned
|
||||||
|
* {@code ModelSource} will be set on the given {@code dependency}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param dependency The dependency coordinates to resolve, must not be {@code null}.
|
||||||
|
*
|
||||||
|
* @return The source of the requested POM, never {@code null}.
|
||||||
|
*
|
||||||
|
* @throws ModelResolverException If the POM could not be resolved from any configured repository.
|
||||||
|
*
|
||||||
|
* @see Dependency#clone()
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
ModelSource resolveModel(
|
||||||
|
@Nonnull Session session, @Nonnull Dependency dependency, @Nonnull AtomicReference<Dependency> modified)
|
||||||
|
throws ModelResolverException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a repository to use for subsequent resolution requests. The order in which repositories are added matters,
|
||||||
|
* repositories that were added first should also be searched first. When multiple repositories with the same
|
||||||
|
* identifier are added, only the first repository being added will be used.
|
||||||
|
*
|
||||||
|
* @param repository The repository to add to the internal search chain, must not be {@code null}.
|
||||||
|
* @throws ModelResolverException If the repository could not be added (e.g. due to invalid URL or layout).
|
||||||
|
*/
|
||||||
|
void addRepository(@Nonnull Session session, Repository repository) throws ModelResolverException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a repository to use for subsequent resolution requests. The order in which repositories are added matters,
|
||||||
|
* repositories that were added first should also be searched first. When multiple repositories with the same
|
||||||
|
* identifier are added, then the value of the replace argument determines the behaviour.
|
||||||
|
*
|
||||||
|
* If replace is false then any existing repository with the same Id will remain in use. If replace
|
||||||
|
* is true the new repository replaces the original.
|
||||||
|
*
|
||||||
|
* @param repository The repository to add to the internal search chain, must not be {@code null}.
|
||||||
|
* @throws ModelResolverException If the repository could not be added (e.g. due to invalid URL or layout).
|
||||||
|
*/
|
||||||
|
void addRepository(@Nonnull Session session, Repository repository, boolean replace) throws ModelResolverException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones this resolver for usage in a forked resolution process. In general, implementors need not provide a deep
|
||||||
|
* clone. The only requirement is that invocations of {@link #addRepository(Session, Repository)} on the clone do not affect
|
||||||
|
* the state of the original resolver and vice versa.
|
||||||
|
*
|
||||||
|
* @return The cloned resolver, never {@code null}.
|
||||||
|
*/
|
||||||
|
ModelResolver newCopy();
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals an error when resolving the path to an external model.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ModelResolverException extends MavenException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The group id of the unresolvable model.
|
||||||
|
*/
|
||||||
|
private final String groupId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The artifact id of the unresolvable model.
|
||||||
|
*/
|
||||||
|
private final String artifactId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version of the unresolvable model.
|
||||||
|
*/
|
||||||
|
private final String version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new exception with specified detail message and cause.
|
||||||
|
*
|
||||||
|
* @param message The detail message, may be {@code null}.
|
||||||
|
* @param groupId The group id of the unresolvable model, may be {@code null}.
|
||||||
|
* @param artifactId The artifact id of the unresolvable model, may be {@code null}.
|
||||||
|
* @param version The version of the unresolvable model, may be {@code null}.
|
||||||
|
* @param cause The cause, may be {@code null}.
|
||||||
|
*/
|
||||||
|
public ModelResolverException(String message, String groupId, String artifactId, String version, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
this.groupId = (groupId != null) ? groupId : "";
|
||||||
|
this.artifactId = (artifactId != null) ? artifactId : "";
|
||||||
|
this.version = (version != null) ? version : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new exception with specified detail message.
|
||||||
|
*
|
||||||
|
* @param message The detail message, may be {@code null}.
|
||||||
|
* @param groupId The group id of the unresolvable model, may be {@code null}.
|
||||||
|
* @param artifactId The artifact id of the unresolvable model, may be {@code null}.
|
||||||
|
* @param version The version of the unresolvable model, may be {@code null}.
|
||||||
|
*/
|
||||||
|
public ModelResolverException(String message, String groupId, String artifactId, String version) {
|
||||||
|
super(message);
|
||||||
|
this.groupId = (groupId != null) ? groupId : "";
|
||||||
|
this.artifactId = (artifactId != null) ? artifactId : "";
|
||||||
|
this.version = (version != null) ? version : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new exception with specified cause
|
||||||
|
*
|
||||||
|
* @param cause
|
||||||
|
* @param groupId
|
||||||
|
* @param artifactId
|
||||||
|
* @param version
|
||||||
|
*/
|
||||||
|
public ModelResolverException(Throwable cause, String groupId, String artifactId, String version) {
|
||||||
|
super(cause);
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.artifactId = artifactId;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the group id of the unresolvable model.
|
||||||
|
*
|
||||||
|
* @return The group id of the unresolvable model, can be empty but never {@code null}.
|
||||||
|
*/
|
||||||
|
public String getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the artifact id of the unresolvable model.
|
||||||
|
*
|
||||||
|
* @return The artifact id of the unresolvable model, can be empty but never {@code null}.
|
||||||
|
*/
|
||||||
|
public String getArtifactId() {
|
||||||
|
return artifactId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the version of the unresolvable model.
|
||||||
|
*
|
||||||
|
* @return The version of the unresolvable model, can be empty but never {@code null}.
|
||||||
|
*/
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.apache.maven.api.annotations.Nonnull;
|
||||||
|
import org.apache.maven.api.annotations.Nullable;
|
||||||
|
|
||||||
|
import static org.apache.maven.api.services.BaseRequest.nonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Source specific to load POMs. The {@link #resolve(ModelLocator, String)} method
|
||||||
|
* will be used to find POMs for children modules.
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
public interface ModelSource extends Source {
|
||||||
|
|
||||||
|
interface ModelLocator {
|
||||||
|
/**
|
||||||
|
* Returns the file containing the pom or null if a pom can not be
|
||||||
|
* found at the given file or in the given directory.
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
Path locateExistingPom(@Nonnull Path project);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
ModelSource resolve(@Nonnull ModelLocator modelLocator, @Nonnull String relative);
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
static ModelSource fromPath(@Nonnull Path path) {
|
||||||
|
return fromPath(path, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
static ModelSource fromPath(@Nonnull Path path, @Nullable String location) {
|
||||||
|
return new PathSource(nonNull(path, "path cannot be null"), location);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.apache.maven.api.annotations.Nonnull;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ModelTransformer is a way to transform the local pom while streaming the input.
|
||||||
|
*
|
||||||
|
* The {@link #transform(ModelTransformerContext, Model, Path)} method uses a Path on purpose, to ensure the
|
||||||
|
* local pom is the original source.
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
public interface ModelTransformer {
|
||||||
|
/**
|
||||||
|
* @param context the context, cannot be null
|
||||||
|
* @param model the model to transform
|
||||||
|
* @param path the pom file, cannot be null
|
||||||
|
* @throws ModelTransformerException if the transformation fails
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Model transform(@Nonnull ModelTransformerContext context, @Nonnull Model model, @Nonnull Path path)
|
||||||
|
throws ModelTransformerException;
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context used to transform a pom file.
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
public interface ModelTransformerContext {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key to get the TransformerContext from the SessionData
|
||||||
|
*/
|
||||||
|
Object KEY = ModelTransformerContext.class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the Maven user property.
|
||||||
|
*/
|
||||||
|
String getUserProperty(String key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the model based on the path when resolving the parent based on relativePath.
|
||||||
|
*
|
||||||
|
* @param from the requiring model
|
||||||
|
* @param pomFile the path to the pomFile
|
||||||
|
* @return the model, otherwise {@code null}
|
||||||
|
*/
|
||||||
|
Model getRawModel(Path from, Path pomFile);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the model from the reactor based on the groupId and artifactId when resolving reactor dependencies.
|
||||||
|
*
|
||||||
|
* @param from the requiring model
|
||||||
|
* @param groupId the groupId
|
||||||
|
* @param artifactId the artifactId
|
||||||
|
* @return the model, otherwise {@code null}
|
||||||
|
* @throws IllegalStateException if multiple versions of the same GA are part of the reactor
|
||||||
|
*/
|
||||||
|
Model getRawModel(Path from, String groupId, String artifactId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locate the POM file inside the given directory.
|
||||||
|
*/
|
||||||
|
Path locate(Path path);
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The transformerContextBuilder is responsible for initializing the TransformerContext.
|
||||||
|
* In case rawModels are missing, it could do new buildingRequests on the ModelBuilder.
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
public interface ModelTransformerContextBuilder {
|
||||||
|
/**
|
||||||
|
* This method is used to initialize the TransformerContext
|
||||||
|
*
|
||||||
|
* @param request the modelBuildingRequest
|
||||||
|
* @param problems the problemCollector
|
||||||
|
* @return the mutable transformerContext
|
||||||
|
*/
|
||||||
|
ModelTransformerContext initialize(ModelBuilderRequest request, ModelProblemCollector problems);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The immutable transformerContext, can be used after the buildplan is finished.
|
||||||
|
*
|
||||||
|
* @return the immutable transformerContext
|
||||||
|
*/
|
||||||
|
ModelTransformerContext build();
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
public class ModelTransformerException extends MavenException {
|
||||||
|
|
||||||
|
public ModelTransformerException(Exception e) {
|
||||||
|
super(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelTransformerException(String message, Throwable exception) {
|
||||||
|
super(message, exception);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,17 +18,25 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.maven.api.services;
|
package org.apache.maven.api.services;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
class PathSource implements Source {
|
class PathSource implements ModelSource {
|
||||||
|
|
||||||
private final Path path;
|
private final Path path;
|
||||||
|
private final String location;
|
||||||
|
|
||||||
PathSource(Path path) {
|
PathSource(Path path) {
|
||||||
|
this(path, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
PathSource(Path path, String location) {
|
||||||
this.path = path;
|
this.path = path;
|
||||||
|
this.location = location != null ? location : path.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -43,11 +51,32 @@ class PathSource implements Source {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getLocation() {
|
public String getLocation() {
|
||||||
return path.toString();
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Source resolve(String relative) {
|
public Source resolve(String relative) {
|
||||||
return new PathSource(path.resolve(relative));
|
return new PathSource(path.resolve(relative));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelSource resolve(ModelLocator locator, String relative) {
|
||||||
|
String norm = relative.replace('\\', File.separatorChar).replace('/', File.separatorChar);
|
||||||
|
Path path = getPath().getParent().resolve(norm);
|
||||||
|
Path relatedPom = locator.locateExistingPom(path);
|
||||||
|
if (relatedPom != null) {
|
||||||
|
return new PathSource(relatedPom.normalize(), null);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return this == o || o instanceof PathSource ps && Objects.equals(path, ps.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,8 @@ public interface Source {
|
||||||
* @param relative is the path of the requested source relative to this source
|
* @param relative is the path of the requested source relative to this source
|
||||||
* @return related source or <code>null</code> if no such source
|
* @return related source or <code>null</code> if no such source
|
||||||
*/
|
*/
|
||||||
Source resolve(String relative);
|
@Nullable
|
||||||
|
Source resolve(@Nonnull String relative);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Source for the following Path
|
* Creates a Source for the following Path
|
||||||
|
|
|
@ -39,5 +39,8 @@ public interface TypeRegistry extends ExtensibleEnumRegistry<Type> {
|
||||||
* @return the type
|
* @return the type
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Type require(@Nonnull String id);
|
@Override
|
||||||
|
default Type require(@Nonnull String id) {
|
||||||
|
return lookup(id).orElseThrow(() -> new IllegalArgumentException("Unknown extensible enum value '" + id + "'"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ under the License.
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-xml-impl</artifactId>
|
<artifactId>maven-api-xml</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,11 @@ import org.apache.maven.api.services.Source;
|
||||||
@Consumer
|
@Consumer
|
||||||
public interface ModelParser extends SpiService {
|
public interface ModelParser extends SpiService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option that can be specified in the options map. The value should be a Boolean.
|
||||||
|
*/
|
||||||
|
String STRICT = "strict";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locates the pom in the given directory.
|
* Locates the pom in the given directory.
|
||||||
*
|
*
|
||||||
|
|
|
@ -34,6 +34,14 @@ under the License.
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-api-core</artifactId>
|
<artifactId>maven-api-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-api-spi</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-api-metadata</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-di</artifactId>
|
<artifactId>maven-di</artifactId>
|
||||||
|
@ -52,7 +60,15 @@ under the License.
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-resolver-provider</artifactId>
|
<artifactId>maven-xml-impl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven.resolver</groupId>
|
||||||
|
<artifactId>maven-resolver-impl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.plexus</groupId>
|
||||||
|
<artifactId>plexus-interpolation</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -60,6 +76,21 @@ under the License.
|
||||||
<artifactId>mockito-junit-jupiter</artifactId>
|
<artifactId>mockito-junit-jupiter</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven.resolver</groupId>
|
||||||
|
<artifactId>maven-resolver-connector-basic</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven.resolver</groupId>
|
||||||
|
<artifactId>maven-resolver-transport-file</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven.resolver</groupId>
|
||||||
|
<artifactId>maven-resolver-transport-apache</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -123,6 +154,59 @@ under the License.
|
||||||
</params>
|
</params>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>velocity-metadata</id>
|
||||||
|
<goals>
|
||||||
|
<goal>velocity</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>generate-sources</phase>
|
||||||
|
<configuration>
|
||||||
|
<version>1.1.0</version>
|
||||||
|
<basedir>${project.basedir}/../api/maven-api-metadata</basedir>
|
||||||
|
<velocityBasedir>${project.basedir}/../src/mdo</velocityBasedir>
|
||||||
|
<models>
|
||||||
|
<model>src/main/mdo/metadata.mdo</model>
|
||||||
|
</models>
|
||||||
|
<templates>
|
||||||
|
<template>reader-stax.vm</template>
|
||||||
|
<template>writer-stax.vm</template>
|
||||||
|
</templates>
|
||||||
|
<params>
|
||||||
|
<param>packageModelV4=org.apache.maven.api.metadata</param>
|
||||||
|
<param>packageToolV4=org.apache.maven.metadata.v4</param>
|
||||||
|
</params>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>model-v4</id>
|
||||||
|
<goals>
|
||||||
|
<goal>velocity</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>generate-sources</phase>
|
||||||
|
<configuration>
|
||||||
|
<version>4.1.0</version>
|
||||||
|
<basedir>${project.basedir}/../api/maven-api-model</basedir>
|
||||||
|
<velocityBasedir>${project.basedir}/../src/mdo</velocityBasedir>
|
||||||
|
<models>
|
||||||
|
<model>src/main/mdo/maven.mdo</model>
|
||||||
|
</models>
|
||||||
|
<templates>
|
||||||
|
<template>merger.vm</template>
|
||||||
|
<template>transformer.vm</template>
|
||||||
|
<template>reader-stax.vm</template>
|
||||||
|
<template>writer-stax.vm</template>
|
||||||
|
<template>model-version.vm</template>
|
||||||
|
</templates>
|
||||||
|
<params>
|
||||||
|
<param>forcedIOModelVersion=4.0.0</param>
|
||||||
|
<param>packageModelV3=org.apache.maven.model</param>
|
||||||
|
<param>packageModelV4=org.apache.maven.api.model</param>
|
||||||
|
<param>packageToolV4=org.apache.maven.model.v4</param>
|
||||||
|
<param>isMavenModel=true</param>
|
||||||
|
<param>minimalVersion=4.0.0</param>
|
||||||
|
</params>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
<id>modello-site-docs</id>
|
<id>modello-site-docs</id>
|
||||||
<phase>none</phase>
|
<phase>none</phase>
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.DependencyManagement;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the import of dependency management from other models into the target model.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface DependencyManagementImporter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports the specified dependency management sections into the given target model.
|
||||||
|
*
|
||||||
|
* @param target The model into which to import the dependency management section, must not be <code>null</code>.
|
||||||
|
* @param sources The dependency management sections to import, may be <code>null</code>.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
Model importManagement(
|
||||||
|
Model target,
|
||||||
|
List<? extends DependencyManagement> sources,
|
||||||
|
ModelBuilderRequest request,
|
||||||
|
ModelProblemCollector problems);
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles injection of dependency management into the model.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface DependencyManagementInjector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges default values from the dependency management section of the given model into itself.
|
||||||
|
*
|
||||||
|
* @param model The model into which to merge the values specified by its dependency management sections, must not
|
||||||
|
* be <code>null</code>.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
Model injectManagement(Model model, ModelBuilderRequest request, ModelProblemCollector problems);
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles inheritance of model values.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface InheritanceAssembler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges values from the specified parent model into the given child model. Implementations are expected to keep
|
||||||
|
* parent and child completely decoupled by injecting deep copies of objects into the child rather than the original
|
||||||
|
* objects from the parent.
|
||||||
|
*
|
||||||
|
* @param child The child model into which to merge the values inherited from the parent, must not be
|
||||||
|
* <code>null</code>.
|
||||||
|
* @param parent The (read-only) parent model from which to inherit the values, may be <code>null</code>.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
Model assembleModelInheritance(
|
||||||
|
Model child, Model parent, ModelBuilderRequest request, ModelProblemCollector problems);
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles injection of plugin executions induced by the lifecycle bindings for a packaging.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface LifecycleBindingsInjector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects plugin executions induced by lifecycle bindings into the specified model. The model has already undergone
|
||||||
|
* injection of plugin management so any plugins that are injected by lifecycle bindings and are not already present
|
||||||
|
* in the model's plugin section need to be subjected to the model's plugin management.
|
||||||
|
*
|
||||||
|
* @param model The model into which to inject the default plugin executions for its packaging, must not be
|
||||||
|
* <code>null</code>.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
Model injectLifecycleBindings(Model model, ModelBuilderRequest request, ModelProblemCollector problems);
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds data relevant for a model building event.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ModelBuildingEvent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the model being built. The precise state of this model depends on the event being fired.
|
||||||
|
*
|
||||||
|
* @return The model being built, never {@code null}.
|
||||||
|
*/
|
||||||
|
Model model();
|
||||||
|
|
||||||
|
Consumer<Model> update();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the model building request being processed.
|
||||||
|
*
|
||||||
|
* @return The model building request being processed, never {@code null}.
|
||||||
|
*/
|
||||||
|
ModelBuilderRequest request();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the container used to collect problems that were encountered while processing the event.
|
||||||
|
*
|
||||||
|
* @return The container used to collect problems that were encountered, never {@code null}.
|
||||||
|
*/
|
||||||
|
ModelProblemCollector problems();
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines events that the model builder fires during construction of the effective model. When a listener encounters
|
||||||
|
* errors while processing the event, it can report these problems via {@link ModelBuildingEvent#problems()}.
|
||||||
|
*/
|
||||||
|
public interface ModelBuildingListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the listener that the model has been constructed to the extent where build extensions can be processed.
|
||||||
|
*
|
||||||
|
* @param event The details about the event.
|
||||||
|
*/
|
||||||
|
default void buildExtensionsAssembled(ModelBuildingEvent event) {}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.apache.maven.api.services.Source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caches auxiliary data used during model building like already processed raw/effective models. The data in the cache
|
||||||
|
* is meant for exclusive consumption by the model builder and is opaque to the cache implementation. The cache key is
|
||||||
|
* formed by a combination of group id, artifact id, version and tag. The first three components generally refer to the
|
||||||
|
* identity of a model. The tag allows for further classification of the associated data on the sole discretion of the
|
||||||
|
* model builder.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ModelCache {
|
||||||
|
|
||||||
|
<T> T computeIfAbsent(String groupId, String artifactId, String version, String tag, Supplier<T> data);
|
||||||
|
|
||||||
|
<T> T computeIfAbsent(Source path, String tag, Supplier<T> data);
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces expressions of the form <code>${token}</code> with their effective values. Effective values are basically
|
||||||
|
* calculated from the elements of the model itself and the execution properties from the building request.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ModelInterpolator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interpolates expressions in the specified model. Note that implementations are free to either interpolate the
|
||||||
|
* provided model directly or to create a clone of the model and interpolate the clone. Callers should always use
|
||||||
|
* the returned model and must not rely on the input model being updated.
|
||||||
|
*
|
||||||
|
* @param model The model to interpolate, must not be {@code null}.
|
||||||
|
* @param projectDir The project directory, may be {@code null} if the model does not belong to a local project but
|
||||||
|
* to some artifact's metadata.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
* @return The interpolated model, never {@code null}.
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
Model interpolateModel(Model model, Path projectDir, ModelBuilderRequest request, ModelProblemCollector problems);
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles normalization of a model. In this context, normalization is the process of producing a canonical
|
||||||
|
* representation for models that physically look different but are semantically equivalent.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ModelNormalizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges duplicate elements like multiple declarations of the same build plugin in the specified model.
|
||||||
|
*
|
||||||
|
* @param model The model whose duplicate elements should be merged, must not be {@code null}.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
Model mergeDuplicates(Model model, ModelBuilderRequest request, ModelProblemCollector problems);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets default values in the specified model that for technical reasons cannot be set directly in the Modello
|
||||||
|
* definition.
|
||||||
|
*
|
||||||
|
* @param model The model in which to set the default values, must not be {@code null}.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
Model injectDefaultValues(Model model, ModelBuilderRequest request, ModelProblemCollector problems);
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves relative paths of a model against a specific base directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ModelPathTranslator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the well-known paths of the specified model against the given base directory. Paths within plugin
|
||||||
|
* configuration are not processed.
|
||||||
|
*
|
||||||
|
* @param model The model whose paths should be resolved, may be {@code null}.
|
||||||
|
* @param basedir The base directory to resolve relative paths against, may be {@code null}.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
Model alignToBaseDirectory(Model model, Path basedir, ModelBuilderRequest request);
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.apache.maven.api.annotations.Nonnull;
|
||||||
|
import org.apache.maven.api.annotations.Nullable;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.xml.XmlReaderException;
|
||||||
|
import org.apache.maven.api.services.xml.XmlReaderRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ModelProcessor
|
||||||
|
*/
|
||||||
|
public interface ModelProcessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file containing the pom to be parsed or null if a pom can not be found
|
||||||
|
* at the given file or in the given directory.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
Path locateExistingPom(@Nonnull Path project);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the model from the specified byte stream. The stream will be automatically closed before the method
|
||||||
|
* returns.
|
||||||
|
*
|
||||||
|
* @param request The reader request to deserialize the model, must not be {@code null}.
|
||||||
|
* @return The deserialized model, never {@code null}.
|
||||||
|
* @throws IOException If the model could not be deserialized.
|
||||||
|
* @throws XmlReaderException If the input format could not be parsed.
|
||||||
|
*/
|
||||||
|
Model read(XmlReaderRequest request) throws IOException, XmlReaderException;
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes URLs to remove the ugly parent references "../" that got potentially inserted by URL adjustment during
|
||||||
|
* model inheritance.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ModelUrlNormalizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes the well-known URLs of the specified model.
|
||||||
|
*
|
||||||
|
* @param model The model whose URLs should be normalized, may be {@code null}.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
Model normalize(Model model, ModelBuilderRequest request);
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the model for missing or invalid values.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ModelValidator {
|
||||||
|
/**
|
||||||
|
* Checks the specified file model for missing or invalid values. This model is directly created from the POM
|
||||||
|
* file and has not been subjected to inheritance, interpolation or profile/default injection.
|
||||||
|
*
|
||||||
|
* @param model The model to validate, must not be {@code null}.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
default void validateFileModel(Model model, ModelBuilderRequest request, ModelProblemCollector problems) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the specified (raw) model for missing or invalid values. The raw model is the file model + buildpom filter
|
||||||
|
* transformation and has not been subjected to inheritance, interpolation or profile/default injection.
|
||||||
|
*
|
||||||
|
* @param model The model to validate, must not be {@code null}.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
void validateRawModel(Model model, ModelBuilderRequest request, ModelProblemCollector problems);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the specified (effective) model for missing or invalid values. The effective model is fully assembled and
|
||||||
|
* has undergone inheritance, interpolation and other model operations.
|
||||||
|
*
|
||||||
|
* @param model The model to validate, must not be {@code null}.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
void validateEffectiveModel(Model model, ModelBuilderRequest request, ModelProblemCollector problems);
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import org.apache.maven.api.Version;
|
||||||
|
import org.apache.maven.api.VersionConstraint;
|
||||||
|
import org.apache.maven.api.VersionRange;
|
||||||
|
import org.apache.maven.api.annotations.Nonnull;
|
||||||
|
import org.apache.maven.api.services.VersionParserException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model builder specific version parser. It is intentionally not
|
||||||
|
* {@link org.apache.maven.api.services.VersionParser} as this is not a service,
|
||||||
|
* but at Maven runtime it MAY actually use that service.
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
public interface ModelVersionParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the specified version string, for example "1.0".
|
||||||
|
*
|
||||||
|
* @param version the version string to parse, must not be {@code null}
|
||||||
|
* @return the parsed version, never {@code null}
|
||||||
|
* @throws VersionParserException if the string violates the syntax rules of this scheme
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Version parseVersion(@Nonnull String version);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the specified version range specification, for example "[1.0,2.0)".
|
||||||
|
*
|
||||||
|
* @param range the range specification to parse, must not be {@code null}
|
||||||
|
* @return the parsed version range, never {@code null}
|
||||||
|
* @throws VersionParserException if the range specification violates the syntax rules of this scheme
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
VersionRange parseVersionRange(@Nonnull String range);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the specified version constraint specification, for example "1.0" or "[1.0,2.0)".
|
||||||
|
*
|
||||||
|
* @param constraint the range specification to parse, must not be {@code null}
|
||||||
|
* @return the parsed version constraint, never {@code null}
|
||||||
|
* @throws VersionParserException if the range specification violates the syntax rules of this scheme
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
VersionConstraint parseVersionConstraint(@Nonnull String constraint);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a given artifact version is considered a {@code SNAPSHOT} or not.
|
||||||
|
*/
|
||||||
|
boolean isSnapshot(@Nonnull String version);
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows a fixed set of properties that are valid inside a version and that could be overwritten for example on the
|
||||||
|
* commandline
|
||||||
|
*/
|
||||||
|
public interface ModelVersionProcessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param property the property to check
|
||||||
|
* @return <code>true</code> if this is a valid property for this processor
|
||||||
|
*/
|
||||||
|
boolean isValidProperty(String property);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is responsible for examining the request and possibly overwrite of the valid properties in the model
|
||||||
|
*
|
||||||
|
* @param modelProperties
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
void overwriteModelProperties(Properties modelProperties, ModelBuilderRequest request);
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves relative paths against a specific base directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface PathTranslator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the specified path against the given base directory. The resolved path will be absolute and uses the
|
||||||
|
* platform-specific file separator if a base directory is given. Otherwise, the input path will be returned
|
||||||
|
* unaltered.
|
||||||
|
*
|
||||||
|
* @param path The path to resolve, may be {@code null}.
|
||||||
|
* @param basedir The base directory to resolve relative paths against, may be {@code null}.
|
||||||
|
* @return The resolved path or {@code null} if the input path was {@code null}.
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
String alignToBaseDirectory(String path, Path basedir);
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles expansion of general build plugin configuration into individual executions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface PluginConfigurationExpander {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges values from general build plugin configuration into the individual plugin executions of the given model.
|
||||||
|
*
|
||||||
|
* @param model The model whose build plugin configuration should be expanded, must not be <code>null</code>.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
Model expandPluginConfiguration(Model model, ModelBuilderRequest request, ModelProblemCollector problems);
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles injection of plugin management into the model.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface PluginManagementInjector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges default values from the plugin management section of the given model into itself.
|
||||||
|
*
|
||||||
|
* @param model The model into which to merge the values specified by its plugin management section, must not be
|
||||||
|
* <code>null</code>.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
Model injectManagement(Model model, ModelBuilderRequest request, ModelProblemCollector problems);
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the environmental context used to determine the activation status of profiles.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ProfileActivationContext {
|
||||||
|
/**
|
||||||
|
* Key of the property containing the project's packaging.
|
||||||
|
* Available in {@link #getUserProperties()}.
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
String PROPERTY_NAME_PACKAGING = "packaging";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the identifiers of those profiles that should be activated by explicit demand.
|
||||||
|
*
|
||||||
|
* @return The identifiers of those profiles to activate, never {@code null}.
|
||||||
|
*/
|
||||||
|
List<String> getActiveProfileIds();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the identifiers of those profiles that should be deactivated by explicit demand.
|
||||||
|
*
|
||||||
|
* @return The identifiers of those profiles to deactivate, never {@code null}.
|
||||||
|
*/
|
||||||
|
List<String> getInactiveProfileIds();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the system properties to use for interpolation and profile activation. The system properties are collected
|
||||||
|
* from the runtime environment like {@link System#getProperties()} and environment variables.
|
||||||
|
*
|
||||||
|
* @return The execution properties, never {@code null}.
|
||||||
|
*/
|
||||||
|
Map<String, String> getSystemProperties();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the user properties to use for interpolation and profile activation. The user properties have been
|
||||||
|
* configured directly by the user on his discretion, e.g. via the {@code -Dkey=value} parameter on the command
|
||||||
|
* line.
|
||||||
|
*
|
||||||
|
* @return The user properties, never {@code null}.
|
||||||
|
*/
|
||||||
|
Map<String, String> getUserProperties();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the base directory of the current project (if any).
|
||||||
|
*
|
||||||
|
* @return The base directory of the current project or {@code null} if none.
|
||||||
|
*/
|
||||||
|
Path getProjectDirectory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets current calculated project properties
|
||||||
|
*
|
||||||
|
* @return The project properties, never {@code null}.
|
||||||
|
*/
|
||||||
|
Map<String, String> getProjectProperties();
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a profile should be activated.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ProfileActivator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the specified profile is active in the given activator context.
|
||||||
|
*
|
||||||
|
* @param profile The profile whose activation status should be determined, must not be {@code null}.
|
||||||
|
* @param context The environmental context used to determine the activation status of the profile, must not be
|
||||||
|
* {@code null}.
|
||||||
|
* @param problems The container used to collect problems (e.g. bad syntax) that were encountered, must not be
|
||||||
|
* {@code null}.
|
||||||
|
* @return {@code true} if the profile is active, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether specified activation method is present in configuration or not. It should help to have AND
|
||||||
|
* between activation conditions
|
||||||
|
* Need for solving https://issues.apache.org/jira/browse/MNG-4565
|
||||||
|
* @param profile The profile whose activation status should be determined, must not be {@code null}.
|
||||||
|
* @param context The environmental context used to determine the activation status of the profile, must not be
|
||||||
|
* {@code null}.
|
||||||
|
* @param problems The container used to collect problems (e.g. bad syntax) that were encountered, must not be
|
||||||
|
* {@code null}.
|
||||||
|
* @return {@code true} if the profile is active, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
boolean presentInConfig(Profile profile, ProfileActivationContext context, ModelProblemCollector problems);
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles profile injection into the model.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ProfileInjector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges values from the specified profile into the given model. Implementations are expected to keep the profile
|
||||||
|
* and model completely decoupled by injecting deep copies rather than the original objects from the profile.
|
||||||
|
*
|
||||||
|
* @param model The model into which to merge the values defined by the profile, must not be <code>null</code>.
|
||||||
|
* @param profile The (read-only) profile whose values should be injected, may be <code>null</code>.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
default Model injectProfile(
|
||||||
|
Model model, Profile profile, ModelBuilderRequest request, ModelProblemCollector problems) {
|
||||||
|
return injectProfiles(model, List.of(profile), request, problems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges values from the specified profile into the given model. Implementations are expected to keep the profile
|
||||||
|
* and model completely decoupled by injecting deep copies rather than the original objects from the profile.
|
||||||
|
*
|
||||||
|
* @param model The model into which to merge the values defined by the profile, must not be <code>null</code>.
|
||||||
|
* @param profiles The (read-only) list of profiles whose values should be injected, must not be <code>null</code>.
|
||||||
|
* @param request The model building request that holds further settings, must not be {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
*/
|
||||||
|
Model injectProfiles(
|
||||||
|
Model model, List<Profile> profiles, ModelBuilderRequest request, ModelProblemCollector problems);
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the active profiles among a given collection of profiles.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ProfileSelector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the profiles which are active in the specified activation context. Active profiles will eventually be
|
||||||
|
* injected into the model.
|
||||||
|
*
|
||||||
|
* @param profiles The profiles whose activation status should be determined, must not be {@code null}.
|
||||||
|
* @param context The environmental context used to determine the activation status of a profile, must not be
|
||||||
|
* {@code null}.
|
||||||
|
* @param problems The container used to collect problems that were encountered, must not be {@code null}.
|
||||||
|
* @return The profiles that have been activated, never {@code null}.
|
||||||
|
*/
|
||||||
|
List<Profile> getActiveProfiles(
|
||||||
|
Collection<Profile> profiles, ProfileActivationContext context, ModelProblemCollector problems);
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.apache.maven.api.annotations.Nonnull;
|
||||||
|
import org.apache.maven.api.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface used to locate the root directory for a given project.
|
||||||
|
*
|
||||||
|
* The root locator is usually looked up from the plexus container.
|
||||||
|
* One notable exception is the computation of the early {@code session.rootDirectory}
|
||||||
|
* property which happens very early. The implementation used in this case
|
||||||
|
* will be discovered using the JDK service mechanism.
|
||||||
|
*
|
||||||
|
* The default implementation will look for a {@code .mvn} child directory
|
||||||
|
* or a {@code pom.xml} containing the {@code root="true"} attribute.
|
||||||
|
*/
|
||||||
|
public interface RootLocator {
|
||||||
|
|
||||||
|
String UNABLE_TO_FIND_ROOT_PROJECT_MESSAGE = "Unable to find the root directory. "
|
||||||
|
+ "Create a .mvn directory in the root directory or add the root=\"true\""
|
||||||
|
+ " attribute on the root project's model to identify it.";
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
default Path findMandatoryRoot(Path basedir) {
|
||||||
|
Path rootDirectory = findRoot(basedir);
|
||||||
|
if (rootDirectory == null) {
|
||||||
|
throw new IllegalStateException(getNoRootMessage());
|
||||||
|
}
|
||||||
|
return rootDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
default Path findRoot(Path basedir) {
|
||||||
|
Path rootDirectory = basedir;
|
||||||
|
while (rootDirectory != null && !isRootDirectory(rootDirectory)) {
|
||||||
|
rootDirectory = rootDirectory.getParent();
|
||||||
|
}
|
||||||
|
return rootDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
default String getNoRootMessage() {
|
||||||
|
return UNABLE_TO_FIND_ROOT_PROJECT_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isRootDirectory(Path dir);
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
public interface UrlNormalizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes the specified URL.
|
||||||
|
*
|
||||||
|
* @param url The URL to normalize, may be {@code null}.
|
||||||
|
* @return The normalized URL or {@code null} if the input was {@code null}.
|
||||||
|
*/
|
||||||
|
String normalize(String url);
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.api.services.model;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WorkspaceModelResolver
|
||||||
|
*/
|
||||||
|
public interface WorkspaceModelResolver {
|
||||||
|
|
||||||
|
Model resolveRawModel(String groupId, String artifactId, String versionConstraint);
|
||||||
|
|
||||||
|
Model resolveEffectiveModel(String groupId, String artifactId, String versionConstraint);
|
||||||
|
}
|
|
@ -90,10 +90,11 @@ public abstract class AbstractSession implements InternalSession {
|
||||||
List<RemoteRepository> repositories,
|
List<RemoteRepository> repositories,
|
||||||
List<org.eclipse.aether.repository.RemoteRepository> resolverRepositories,
|
List<org.eclipse.aether.repository.RemoteRepository> resolverRepositories,
|
||||||
Lookup lookup) {
|
Lookup lookup) {
|
||||||
this.session = session;
|
this.session = nonNull(session, "session");
|
||||||
this.repositorySystem = repositorySystem;
|
this.repositorySystem = repositorySystem;
|
||||||
this.repositories = getRepositories(repositories, resolverRepositories);
|
this.repositories = getRepositories(repositories, resolverRepositories);
|
||||||
this.lookup = lookup;
|
this.lookup = lookup;
|
||||||
|
this.session.getData().set(InternalSession.class, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -24,7 +24,6 @@ import org.apache.maven.api.Artifact;
|
||||||
import org.apache.maven.api.ArtifactCoordinate;
|
import org.apache.maven.api.ArtifactCoordinate;
|
||||||
import org.apache.maven.api.Version;
|
import org.apache.maven.api.Version;
|
||||||
import org.apache.maven.api.annotations.Nonnull;
|
import org.apache.maven.api.annotations.Nonnull;
|
||||||
import org.apache.maven.repository.internal.DefaultModelVersionParser;
|
|
||||||
|
|
||||||
import static org.apache.maven.internal.impl.Utils.nonNull;
|
import static org.apache.maven.internal.impl.Utils.nonNull;
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.apache.maven.api.Type;
|
||||||
import org.apache.maven.api.Version;
|
import org.apache.maven.api.Version;
|
||||||
import org.apache.maven.api.annotations.Nonnull;
|
import org.apache.maven.api.annotations.Nonnull;
|
||||||
import org.apache.maven.api.annotations.Nullable;
|
import org.apache.maven.api.annotations.Nullable;
|
||||||
import org.apache.maven.repository.internal.DefaultModelVersionParser;
|
|
||||||
import org.eclipse.aether.artifact.ArtifactProperties;
|
import org.eclipse.aether.artifact.ArtifactProperties;
|
||||||
|
|
||||||
import static org.apache.maven.internal.impl.Utils.nonNull;
|
import static org.apache.maven.internal.impl.Utils.nonNull;
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Inject;
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.DistributionManagement;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.Scm;
|
||||||
|
import org.apache.maven.api.model.Site;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.model.ModelUrlNormalizer;
|
||||||
|
import org.apache.maven.api.services.model.UrlNormalizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes URLs to remove the ugly parent references "../" that got potentially inserted by URL adjustment during
|
||||||
|
* model inheritance.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultModelUrlNormalizer implements ModelUrlNormalizer {
|
||||||
|
|
||||||
|
private final UrlNormalizer urlNormalizer;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DefaultModelUrlNormalizer(UrlNormalizer urlNormalizer) {
|
||||||
|
this.urlNormalizer = urlNormalizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model normalize(Model model, ModelBuilderRequest request) {
|
||||||
|
if (model == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Model.Builder builder = Model.newBuilder(model);
|
||||||
|
builder.url(normalize(model.getUrl()));
|
||||||
|
|
||||||
|
Scm scm = model.getScm();
|
||||||
|
if (scm != null) {
|
||||||
|
builder.scm(Scm.newBuilder(scm)
|
||||||
|
.url(normalize(scm.getUrl()))
|
||||||
|
.connection(normalize(scm.getConnection()))
|
||||||
|
.developerConnection(normalize(scm.getDeveloperConnection()))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
DistributionManagement dist = model.getDistributionManagement();
|
||||||
|
if (dist != null) {
|
||||||
|
Site site = dist.getSite();
|
||||||
|
if (site != null) {
|
||||||
|
builder.distributionManagement(dist.withSite(site.withUrl(normalize(site.getUrl()))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalize(String url) {
|
||||||
|
return urlNormalizer.normalize(url);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,288 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.apache.maven.api.Version;
|
||||||
|
import org.apache.maven.api.VersionConstraint;
|
||||||
|
import org.apache.maven.api.VersionRange;
|
||||||
|
import org.apache.maven.api.di.Inject;
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.services.VersionParserException;
|
||||||
|
import org.apache.maven.api.services.model.ModelVersionParser;
|
||||||
|
import org.eclipse.aether.version.InvalidVersionSpecificationException;
|
||||||
|
import org.eclipse.aether.version.VersionScheme;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultModelVersionParser implements ModelVersionParser {
|
||||||
|
private static final String SNAPSHOT = "SNAPSHOT";
|
||||||
|
private static final Pattern SNAPSHOT_TIMESTAMP = Pattern.compile("^(.*-)?([0-9]{8}\\.[0-9]{6}-[0-9]+)$");
|
||||||
|
private final VersionScheme versionScheme;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DefaultModelVersionParser(VersionScheme versionScheme) {
|
||||||
|
this.versionScheme = requireNonNull(versionScheme, "versionScheme");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Version parseVersion(String version) {
|
||||||
|
requireNonNull(version, "version");
|
||||||
|
return new DefaultVersion(versionScheme, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionRange parseVersionRange(String range) {
|
||||||
|
requireNonNull(range, "range");
|
||||||
|
return new DefaultVersionRange(versionScheme, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSnapshot(String version) {
|
||||||
|
return checkSnapshot(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkSnapshot(String version) {
|
||||||
|
return version.endsWith(SNAPSHOT) || SNAPSHOT_TIMESTAMP.matcher(version).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionConstraint parseVersionConstraint(String constraint) {
|
||||||
|
requireNonNull(constraint, "constraint");
|
||||||
|
return new DefaultVersionConstraint(versionScheme, constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DefaultVersion implements Version {
|
||||||
|
private final VersionScheme versionScheme;
|
||||||
|
private final org.eclipse.aether.version.Version delegate;
|
||||||
|
|
||||||
|
DefaultVersion(VersionScheme versionScheme, org.eclipse.aether.version.Version delegate) {
|
||||||
|
this.versionScheme = versionScheme;
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultVersion(VersionScheme versionScheme, String delegateValue) {
|
||||||
|
this.versionScheme = versionScheme;
|
||||||
|
try {
|
||||||
|
this.delegate = versionScheme.parseVersion(delegateValue);
|
||||||
|
} catch (InvalidVersionSpecificationException e) {
|
||||||
|
throw new VersionParserException("Unable to parse version: " + delegateValue, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Version o) {
|
||||||
|
if (o instanceof DefaultVersion) {
|
||||||
|
return delegate.compareTo(((DefaultVersion) o).delegate);
|
||||||
|
} else {
|
||||||
|
return compareTo(new DefaultVersion(versionScheme, o.asString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DefaultVersion that = (DefaultVersion) o;
|
||||||
|
return delegate.equals(that.delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return delegate.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String asString() {
|
||||||
|
return delegate.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return asString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DefaultVersionRange implements VersionRange {
|
||||||
|
private final VersionScheme versionScheme;
|
||||||
|
private final org.eclipse.aether.version.VersionRange delegate;
|
||||||
|
|
||||||
|
DefaultVersionRange(VersionScheme versionScheme, org.eclipse.aether.version.VersionRange delegate) {
|
||||||
|
this.versionScheme = versionScheme;
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultVersionRange(VersionScheme versionScheme, String delegateValue) {
|
||||||
|
this.versionScheme = versionScheme;
|
||||||
|
try {
|
||||||
|
this.delegate = versionScheme.parseVersionRange(delegateValue);
|
||||||
|
} catch (InvalidVersionSpecificationException e) {
|
||||||
|
throw new VersionParserException("Unable to parse version range: " + delegateValue, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Version version) {
|
||||||
|
if (version instanceof DefaultVersion) {
|
||||||
|
return delegate.containsVersion(((DefaultVersion) version).delegate);
|
||||||
|
} else {
|
||||||
|
return contains(new DefaultVersion(versionScheme, version.asString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boundary getUpperBoundary() {
|
||||||
|
org.eclipse.aether.version.VersionRange.Bound bound = delegate.getUpperBound();
|
||||||
|
if (bound == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Boundary() {
|
||||||
|
@Override
|
||||||
|
public Version getVersion() {
|
||||||
|
return new DefaultVersion(versionScheme, bound.getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInclusive() {
|
||||||
|
return bound.isInclusive();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boundary getLowerBoundary() {
|
||||||
|
org.eclipse.aether.version.VersionRange.Bound bound = delegate.getLowerBound();
|
||||||
|
if (bound == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Boundary() {
|
||||||
|
@Override
|
||||||
|
public Version getVersion() {
|
||||||
|
return new DefaultVersion(versionScheme, bound.getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInclusive() {
|
||||||
|
return bound.isInclusive();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String asString() {
|
||||||
|
return delegate.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DefaultVersionRange that = (DefaultVersionRange) o;
|
||||||
|
return delegate.equals(that.delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return delegate.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DefaultVersionConstraint implements VersionConstraint {
|
||||||
|
private final VersionScheme versionScheme;
|
||||||
|
private final org.eclipse.aether.version.VersionConstraint delegate;
|
||||||
|
|
||||||
|
DefaultVersionConstraint(VersionScheme versionScheme, String delegateValue) {
|
||||||
|
this.versionScheme = versionScheme;
|
||||||
|
try {
|
||||||
|
this.delegate = versionScheme.parseVersionConstraint(delegateValue);
|
||||||
|
} catch (InvalidVersionSpecificationException e) {
|
||||||
|
throw new VersionParserException("Unable to parse version constraint: " + delegateValue, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Version version) {
|
||||||
|
if (version instanceof DefaultVersion) {
|
||||||
|
return delegate.containsVersion(((DefaultVersion) version).delegate);
|
||||||
|
} else {
|
||||||
|
return contains(new DefaultVersion(versionScheme, version.asString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String asString() {
|
||||||
|
return delegate.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionRange getVersionRange() {
|
||||||
|
if (delegate.getRange() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new DefaultVersionRange(versionScheme, delegate.getRange());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Version getRecommendedVersion() {
|
||||||
|
if (delegate.getVersion() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new DefaultVersion(versionScheme, delegate.getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DefaultVersionConstraint that = (DefaultVersionConstraint) o;
|
||||||
|
return delegate.equals(that.delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return delegate.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,7 +59,8 @@ public class DefaultModelXmlFactory implements ModelXmlFactory {
|
||||||
try {
|
try {
|
||||||
InputSource source = null;
|
InputSource source = null;
|
||||||
if (request.getModelId() != null || request.getLocation() != null) {
|
if (request.getModelId() != null || request.getLocation() != null) {
|
||||||
source = new InputSource(request.getModelId(), request.getLocation());
|
source = new InputSource(
|
||||||
|
request.getModelId(), path != null ? path.toUri().toString() : null);
|
||||||
}
|
}
|
||||||
MavenStaxReader xml = new MavenStaxReader();
|
MavenStaxReader xml = new MavenStaxReader();
|
||||||
xml.setAddDefaultEntities(request.isAddDefaultEntities());
|
xml.setAddDefaultEntities(request.isAddDefaultEntities());
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Build;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.Plugin;
|
||||||
|
import org.apache.maven.api.model.PluginManagement;
|
||||||
|
import org.apache.maven.api.model.ReportPlugin;
|
||||||
|
import org.apache.maven.api.model.Reporting;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.PluginConfigurationExpander;
|
||||||
|
import org.apache.maven.api.xml.XmlNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles expansion of general build plugin configuration into individual executions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultPluginConfigurationExpander implements PluginConfigurationExpander {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model expandPluginConfiguration(Model model, ModelBuilderRequest request, ModelProblemCollector problems) {
|
||||||
|
Build build = model.getBuild();
|
||||||
|
if (build != null) {
|
||||||
|
build = build.withPlugins(expandPlugin(build.getPlugins()));
|
||||||
|
PluginManagement pluginManagement = build.getPluginManagement();
|
||||||
|
if (pluginManagement != null) {
|
||||||
|
build = build.withPluginManagement(
|
||||||
|
pluginManagement.withPlugins(expandPlugin(pluginManagement.getPlugins())));
|
||||||
|
}
|
||||||
|
model = model.withBuild(build);
|
||||||
|
}
|
||||||
|
Reporting reporting = model.getReporting();
|
||||||
|
if (reporting != null) {
|
||||||
|
expandReport(reporting.getPlugins());
|
||||||
|
}
|
||||||
|
return model.withBuild(build);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Plugin> expandPlugin(List<Plugin> oldPlugins) {
|
||||||
|
return map(oldPlugins, plugin -> {
|
||||||
|
XmlNode pluginConfiguration = plugin.getConfiguration();
|
||||||
|
if (pluginConfiguration != null) {
|
||||||
|
return plugin.withExecutions(map(
|
||||||
|
plugin.getExecutions(),
|
||||||
|
execution -> execution.withConfiguration(
|
||||||
|
XmlNode.merge(execution.getConfiguration(), pluginConfiguration))));
|
||||||
|
} else {
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ReportPlugin> expandReport(List<ReportPlugin> oldPlugins) {
|
||||||
|
return map(oldPlugins, plugin -> {
|
||||||
|
XmlNode pluginConfiguration = plugin.getConfiguration();
|
||||||
|
if (pluginConfiguration != null) {
|
||||||
|
return plugin.withReportSets(map(
|
||||||
|
plugin.getReportSets(),
|
||||||
|
report -> report.withConfiguration(
|
||||||
|
XmlNode.merge(report.getConfiguration(), pluginConfiguration))));
|
||||||
|
} else {
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> List<T> map(List<T> list, Function<T, T> mapper) {
|
||||||
|
List<T> newList = list;
|
||||||
|
for (int i = 0; i < newList.size(); i++) {
|
||||||
|
T oldT = newList.get(i);
|
||||||
|
T newT = mapper.apply(oldT);
|
||||||
|
if (newT != oldT) {
|
||||||
|
if (newList == list) {
|
||||||
|
newList = new ArrayList<>(list);
|
||||||
|
}
|
||||||
|
newList.set(i, newT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newList;
|
||||||
|
}
|
||||||
|
}
|
|
@ -55,4 +55,9 @@ public class DefaultRemoteRepository implements RemoteRepository {
|
||||||
public String getProtocol() {
|
public String getProtocol() {
|
||||||
return repository.getProtocol();
|
return repository.getProtocol();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return repository.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,11 +137,7 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
|
||||||
Settings settings;
|
Settings settings;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
try {
|
try (InputStream is = settingsSource.openStream()) {
|
||||||
InputStream is = settingsSource.openStream();
|
|
||||||
if (is == null) {
|
|
||||||
return Settings.newInstance();
|
|
||||||
}
|
|
||||||
settings = request.getSession()
|
settings = request.getSession()
|
||||||
.getService(SettingsXmlFactory.class)
|
.getService(SettingsXmlFactory.class)
|
||||||
.read(XmlReaderRequest.builder()
|
.read(XmlReaderRequest.builder()
|
||||||
|
@ -150,25 +146,23 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
|
||||||
.strict(true)
|
.strict(true)
|
||||||
.build());
|
.build());
|
||||||
} catch (XmlReaderException e) {
|
} catch (XmlReaderException e) {
|
||||||
InputStream is = settingsSource.openStream();
|
try (InputStream is = settingsSource.openStream()) {
|
||||||
if (is == null) {
|
settings = request.getSession()
|
||||||
return Settings.newInstance();
|
.getService(SettingsXmlFactory.class)
|
||||||
|
.read(XmlReaderRequest.builder()
|
||||||
|
.inputStream(is)
|
||||||
|
.location(settingsSource.getLocation())
|
||||||
|
.strict(false)
|
||||||
|
.build());
|
||||||
|
Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null;
|
||||||
|
problems.add(new DefaultBuilderProblem(
|
||||||
|
settingsSource.getLocation(),
|
||||||
|
loc != null ? loc.getLineNumber() : -1,
|
||||||
|
loc != null ? loc.getColumnNumber() : -1,
|
||||||
|
e,
|
||||||
|
e.getMessage(),
|
||||||
|
BuilderProblem.Severity.WARNING));
|
||||||
}
|
}
|
||||||
settings = request.getSession()
|
|
||||||
.getService(SettingsXmlFactory.class)
|
|
||||||
.read(XmlReaderRequest.builder()
|
|
||||||
.inputStream(is)
|
|
||||||
.location(settingsSource.getLocation())
|
|
||||||
.strict(false)
|
|
||||||
.build());
|
|
||||||
Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null;
|
|
||||||
problems.add(new DefaultBuilderProblem(
|
|
||||||
settingsSource.getLocation(),
|
|
||||||
loc != null ? loc.getLineNumber() : -1,
|
|
||||||
loc != null ? loc.getColumnNumber() : -1,
|
|
||||||
e,
|
|
||||||
e.getMessage(),
|
|
||||||
BuilderProblem.Severity.WARNING));
|
|
||||||
}
|
}
|
||||||
} catch (XmlReaderException e) {
|
} catch (XmlReaderException e) {
|
||||||
Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null;
|
Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null;
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Inject;
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.SuperPomProvider;
|
||||||
|
import org.apache.maven.api.services.model.ModelProcessor;
|
||||||
|
import org.apache.maven.api.services.xml.XmlReaderRequest;
|
||||||
|
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultSuperPomProvider implements SuperPomProvider {
|
||||||
|
|
||||||
|
private final ModelProcessor modelProcessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cached super POM, lazily created.
|
||||||
|
*/
|
||||||
|
private static final Map<String, Model> SUPER_MODELS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DefaultSuperPomProvider(ModelProcessor modelProcessor) {
|
||||||
|
this.modelProcessor = modelProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model getSuperPom(String version) {
|
||||||
|
return SUPER_MODELS.computeIfAbsent(Objects.requireNonNull(version), v -> readModel(version, v));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Model readModel(String version, String v) {
|
||||||
|
String resource = "/org/apache/maven/model/pom-" + v + ".xml";
|
||||||
|
URL url = getClass().getResource(resource);
|
||||||
|
if (url == null) {
|
||||||
|
throw new IllegalStateException("The super POM " + resource + " was not found"
|
||||||
|
+ ", please verify the integrity of your Maven installation");
|
||||||
|
}
|
||||||
|
try (InputStream is = url.openStream()) {
|
||||||
|
String modelId = "org.apache.maven:maven-model-builder:" + version + "-"
|
||||||
|
+ this.getClass().getPackage().getImplementationVersion() + ":super-pom";
|
||||||
|
return modelProcessor.read(XmlReaderRequest.builder()
|
||||||
|
.modelId(modelId)
|
||||||
|
.location(url.toExternalForm())
|
||||||
|
.inputStream(is)
|
||||||
|
.strict(false)
|
||||||
|
.build());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"The super POM " + resource + " is damaged"
|
||||||
|
+ ", please verify the integrity of your Maven installation",
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.services.model.UrlNormalizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes a URL.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultUrlNormalizer implements UrlNormalizer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String normalize(String url) {
|
||||||
|
String result = url;
|
||||||
|
|
||||||
|
if (result != null) {
|
||||||
|
while (true) {
|
||||||
|
int idx = result.indexOf("/../");
|
||||||
|
if (idx < 0) {
|
||||||
|
break;
|
||||||
|
} else if (idx == 0) {
|
||||||
|
result = result.substring(3);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int parent = idx - 1;
|
||||||
|
while (parent >= 0 && result.charAt(parent) == '/') {
|
||||||
|
parent--;
|
||||||
|
}
|
||||||
|
parent = result.lastIndexOf('/', parent);
|
||||||
|
if (parent < 0) {
|
||||||
|
result = result.substring(idx + 4);
|
||||||
|
} else {
|
||||||
|
result = result.substring(0, parent) + result.substring(idx + 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ import org.apache.maven.api.di.Inject;
|
||||||
import org.apache.maven.api.di.Named;
|
import org.apache.maven.api.di.Named;
|
||||||
import org.apache.maven.api.di.Singleton;
|
import org.apache.maven.api.di.Singleton;
|
||||||
import org.apache.maven.api.services.VersionParser;
|
import org.apache.maven.api.services.VersionParser;
|
||||||
import org.apache.maven.model.version.ModelVersionParser;
|
import org.apache.maven.api.services.model.ModelVersionParser;
|
||||||
|
|
||||||
import static org.apache.maven.internal.impl.Utils.nonNull;
|
import static org.apache.maven.internal.impl.Utils.nonNull;
|
||||||
|
|
||||||
|
|
|
@ -66,8 +66,9 @@ public class DefaultVersionRangeResolver implements VersionRangeResolver {
|
||||||
session.toRepositories(session.getRemoteRepositories()),
|
session.toRepositories(session.getRemoteRepositories()),
|
||||||
null));
|
null));
|
||||||
|
|
||||||
Map<String, ArtifactRepository> repos =
|
Map<String, ArtifactRepository> repos = res.getVersions().stream()
|
||||||
res.getVersions().stream().collect(Collectors.toMap(v -> v.toString(), res::getRepository));
|
.filter(v -> res.getRepository(v) != null)
|
||||||
|
.collect(Collectors.toMap(v -> v.toString(), res::getRepository));
|
||||||
|
|
||||||
return new VersionRangeResolverResult() {
|
return new VersionRangeResolverResult() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,21 +18,29 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.maven.internal.impl;
|
package org.apache.maven.internal.impl;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.maven.SessionScoped;
|
import org.apache.maven.api.ExtensibleEnum;
|
||||||
import org.apache.maven.api.*;
|
import org.apache.maven.api.Language;
|
||||||
import org.apache.maven.api.services.*;
|
import org.apache.maven.api.PathScope;
|
||||||
import org.apache.maven.api.spi.*;
|
import org.apache.maven.api.ProjectScope;
|
||||||
|
import org.apache.maven.api.di.Inject;
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.SessionScoped;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.services.ExtensibleEnumRegistry;
|
||||||
|
import org.apache.maven.api.services.LanguageRegistry;
|
||||||
|
import org.apache.maven.api.services.PathScopeRegistry;
|
||||||
|
import org.apache.maven.api.services.ProjectScopeRegistry;
|
||||||
|
import org.apache.maven.api.spi.ExtensibleEnumProvider;
|
||||||
|
import org.apache.maven.api.spi.LanguageProvider;
|
||||||
|
import org.apache.maven.api.spi.PathScopeProvider;
|
||||||
|
import org.apache.maven.api.spi.ProjectScopeProvider;
|
||||||
|
|
||||||
import static org.apache.maven.internal.impl.Utils.nonNull;
|
import static org.apache.maven.internal.impl.Utils.nonNull;
|
||||||
|
|
||||||
|
@ -76,7 +84,8 @@ public class ExtensibleEnumRegistries {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class DefaultExtensibleEnumRegistry<T extends ExtensibleEnum, P extends ExtensibleEnumProvider<T>>
|
public abstract static class DefaultExtensibleEnumRegistry<
|
||||||
|
T extends ExtensibleEnum, P extends ExtensibleEnumProvider<T>>
|
||||||
implements ExtensibleEnumRegistry<T> {
|
implements ExtensibleEnumRegistry<T> {
|
||||||
|
|
||||||
protected final Map<String, T> values;
|
protected final Map<String, T> values;
|
||||||
|
@ -84,12 +93,12 @@ public class ExtensibleEnumRegistries {
|
||||||
DefaultExtensibleEnumRegistry(List<P> providers, T... builtinValues) {
|
DefaultExtensibleEnumRegistry(List<P> providers, T... builtinValues) {
|
||||||
values = Stream.<T>concat(
|
values = Stream.<T>concat(
|
||||||
Stream.of(builtinValues), providers.stream().flatMap(p -> p.provides().stream()))
|
Stream.of(builtinValues), providers.stream().flatMap(p -> p.provides().stream()))
|
||||||
.collect(Collectors.toUnmodifiableMap(ExtensibleEnum::id, Function.identity()));
|
.collect(Collectors.toUnmodifiableMap(t -> t.id().toLowerCase(Locale.ROOT), t -> t));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<T> lookup(String id) {
|
public Optional<T> lookup(String id) {
|
||||||
return Optional.ofNullable(values.get(nonNull(id, "id")));
|
return Optional.ofNullable(values.get(nonNull(id, "id").toLowerCase(Locale.ROOT)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -41,6 +41,10 @@ public interface InternalSession extends Session {
|
||||||
return cast(InternalSession.class, session, "session should be an " + InternalSession.class);
|
return cast(InternalSession.class, session, "session should be an " + InternalSession.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static InternalSession from(org.eclipse.aether.RepositorySystemSession session) {
|
||||||
|
return cast(InternalSession.class, session.getData().get(InternalSession.class), "session");
|
||||||
|
}
|
||||||
|
|
||||||
RemoteRepository getRemoteRepository(org.eclipse.aether.repository.RemoteRepository repository);
|
RemoteRepository getRemoteRepository(org.eclipse.aether.repository.RemoteRepository repository);
|
||||||
|
|
||||||
Node getNode(org.eclipse.aether.graph.DependencyNode node);
|
Node getNode(org.eclipse.aether.graph.DependencyNode node);
|
||||||
|
|
|
@ -40,6 +40,9 @@ class Utils {
|
||||||
|
|
||||||
static <T> T cast(Class<T> clazz, Object o, String name) {
|
static <T> T cast(Class<T> clazz, Object o, String name) {
|
||||||
if (!clazz.isInstance(o)) {
|
if (!clazz.isInstance(o)) {
|
||||||
|
if (o == null) {
|
||||||
|
throw new IllegalArgumentException(name + " is null");
|
||||||
|
}
|
||||||
throw new IllegalArgumentException(name + " is not an instance of " + clazz.getName());
|
throw new IllegalArgumentException(name + " is not an instance of " + clazz.getName());
|
||||||
}
|
}
|
||||||
return clazz.cast(o);
|
return clazz.cast(o);
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Dependency;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.Parent;
|
||||||
|
import org.apache.maven.api.services.ModelTransformer;
|
||||||
|
import org.apache.maven.api.services.ModelTransformerContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ModelSourceTransformer for the build pom
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class BuildModelTransformer implements ModelTransformer {
|
||||||
|
|
||||||
|
public static final String NAMESPACE_PREFIX = "http://maven.apache.org/POM/";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model transform(ModelTransformerContext context, Model model, Path path) {
|
||||||
|
Model.Builder builder = Model.newBuilder(model);
|
||||||
|
handleModelVersion(context, model, path, builder);
|
||||||
|
handleParent(context, model, path, builder);
|
||||||
|
handleReactorDependencies(context, model, path, builder);
|
||||||
|
handleCiFriendlyVersion(context, model, path, builder);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Infer modelVersion from namespace URI
|
||||||
|
//
|
||||||
|
void handleModelVersion(ModelTransformerContext context, Model model, Path pomFile, Model.Builder builder) {
|
||||||
|
String namespace = model.getNamespaceUri();
|
||||||
|
if (model.getModelVersion() == null && namespace != null && namespace.startsWith(NAMESPACE_PREFIX)) {
|
||||||
|
builder.modelVersion(namespace.substring(NAMESPACE_PREFIX.length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Infer parent information
|
||||||
|
//
|
||||||
|
void handleParent(ModelTransformerContext context, Model model, Path pomFile, Model.Builder builder) {
|
||||||
|
Parent parent = model.getParent();
|
||||||
|
if (parent != null) {
|
||||||
|
String version = parent.getVersion();
|
||||||
|
String path = Optional.ofNullable(parent.getRelativePath()).orElse("..");
|
||||||
|
if (version == null && !path.isEmpty()) {
|
||||||
|
Optional<RelativeProject> resolvedParent = resolveRelativePath(
|
||||||
|
pomFile, context, Paths.get(path), parent.getGroupId(), parent.getArtifactId());
|
||||||
|
if (resolvedParent.isPresent()) {
|
||||||
|
version = resolvedParent.get().getVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// CI Friendly version for parent
|
||||||
|
String modVersion = replaceCiFriendlyVersion(context, version);
|
||||||
|
builder.parent(parent.withVersion(modVersion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// CI friendly versions
|
||||||
|
//
|
||||||
|
void handleCiFriendlyVersion(ModelTransformerContext context, Model model, Path pomFile, Model.Builder builder) {
|
||||||
|
String version = model.getVersion();
|
||||||
|
String modVersion = replaceCiFriendlyVersion(context, version);
|
||||||
|
builder.version(modVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Infer inner reactor dependencies version
|
||||||
|
//
|
||||||
|
void handleReactorDependencies(ModelTransformerContext context, Model model, Path pomFile, Model.Builder builder) {
|
||||||
|
List<Dependency> newDeps = new ArrayList<>();
|
||||||
|
boolean modified = false;
|
||||||
|
for (Dependency dep : model.getDependencies()) {
|
||||||
|
if (dep.getVersion() == null) {
|
||||||
|
Model depModel = context.getRawModel(model.getPomFile(), dep.getGroupId(), dep.getArtifactId());
|
||||||
|
if (depModel != null) {
|
||||||
|
String v = depModel.getVersion();
|
||||||
|
if (v == null && depModel.getParent() != null) {
|
||||||
|
v = depModel.getParent().getVersion();
|
||||||
|
}
|
||||||
|
dep = dep.withVersion(v);
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newDeps.add(dep);
|
||||||
|
}
|
||||||
|
if (modified) {
|
||||||
|
builder.dependencies(newDeps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String replaceCiFriendlyVersion(ModelTransformerContext context, String version) {
|
||||||
|
if (version != null) {
|
||||||
|
for (String key : Arrays.asList("changelist", "revision", "sha1")) {
|
||||||
|
String val = context.getUserProperty(key);
|
||||||
|
if (val != null) {
|
||||||
|
version = version.replace("${" + key + "}", val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Optional<RelativeProject> resolveRelativePath(
|
||||||
|
Path pomFile, ModelTransformerContext context, Path relativePath, String groupId, String artifactId) {
|
||||||
|
Path pomPath = pomFile.resolveSibling(relativePath).normalize();
|
||||||
|
if (Files.isDirectory(pomPath)) {
|
||||||
|
pomPath = context.locate(pomPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pomPath == null || !Files.isRegularFile(pomPath)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<RelativeProject> mappedProject = Optional.ofNullable(context.getRawModel(pomFile, pomPath.normalize()))
|
||||||
|
.map(BuildModelTransformer::toRelativeProject);
|
||||||
|
|
||||||
|
if (mappedProject.isPresent()) {
|
||||||
|
RelativeProject project = mappedProject.get();
|
||||||
|
|
||||||
|
if (Objects.equals(groupId, project.getGroupId()) && Objects.equals(artifactId, project.getArtifactId())) {
|
||||||
|
return mappedProject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RelativeProject toRelativeProject(final Model m) {
|
||||||
|
String groupId = m.getGroupId();
|
||||||
|
if (groupId == null && m.getParent() != null) {
|
||||||
|
groupId = m.getParent().getGroupId();
|
||||||
|
}
|
||||||
|
|
||||||
|
String version = m.getVersion();
|
||||||
|
if (version == null && m.getParent() != null) {
|
||||||
|
version = m.getParent().getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RelativeProject(groupId, m.getArtifactId(), version);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class RelativeProject {
|
||||||
|
private final String groupId;
|
||||||
|
|
||||||
|
private final String artifactId;
|
||||||
|
|
||||||
|
private final String version;
|
||||||
|
|
||||||
|
protected RelativeProject(String groupId, String artifactId, String version) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.artifactId = artifactId;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getArtifactId() {
|
||||||
|
return artifactId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Dependency;
|
||||||
|
import org.apache.maven.api.model.DependencyManagement;
|
||||||
|
import org.apache.maven.api.model.Exclusion;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.BuilderProblem.Severity;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblem.Version;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the import of dependency management from other models into the target model.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultDependencyManagementImporter implements DependencyManagementImporter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model importManagement(
|
||||||
|
Model target,
|
||||||
|
List<? extends DependencyManagement> sources,
|
||||||
|
ModelBuilderRequest request,
|
||||||
|
ModelProblemCollector problems) {
|
||||||
|
if (sources != null && !sources.isEmpty()) {
|
||||||
|
Map<String, Dependency> dependencies = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
DependencyManagement depMgmt = target.getDependencyManagement();
|
||||||
|
|
||||||
|
if (depMgmt != null) {
|
||||||
|
for (Dependency dependency : depMgmt.getDependencies()) {
|
||||||
|
dependencies.put(dependency.getManagementKey(), dependency);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
depMgmt = DependencyManagement.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> directDependencies = new HashSet<>(dependencies.keySet());
|
||||||
|
|
||||||
|
for (DependencyManagement source : sources) {
|
||||||
|
for (Dependency dependency : source.getDependencies()) {
|
||||||
|
String key = dependency.getManagementKey();
|
||||||
|
Dependency present = dependencies.putIfAbsent(key, dependency);
|
||||||
|
if (present != null && !equals(dependency, present) && !directDependencies.contains(key)) {
|
||||||
|
// TODO: https://issues.apache.org/jira/browse/MNG-8004
|
||||||
|
problems.add(
|
||||||
|
Severity.WARNING,
|
||||||
|
Version.V40,
|
||||||
|
"Ignored POM import for: " + toString(dependency) + " as already imported "
|
||||||
|
+ toString(present) + ". Add a the conflicting managed dependency directly "
|
||||||
|
+ "to the dependencyManagement section of the POM.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target.withDependencyManagement(depMgmt.withDependencies(dependencies.values()));
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toString(Dependency dependency) {
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder
|
||||||
|
.append(dependency.getGroupId())
|
||||||
|
.append(":")
|
||||||
|
.append(dependency.getArtifactId())
|
||||||
|
.append(":")
|
||||||
|
.append(dependency.getType());
|
||||||
|
if (dependency.getClassifier() != null && !dependency.getClassifier().isEmpty()) {
|
||||||
|
stringBuilder.append(":").append(dependency.getClassifier());
|
||||||
|
}
|
||||||
|
stringBuilder
|
||||||
|
.append(":")
|
||||||
|
.append(dependency.getVersion())
|
||||||
|
.append("@")
|
||||||
|
.append(dependency.getScope() == null ? "compile" : dependency.getScope());
|
||||||
|
if (dependency.isOptional()) {
|
||||||
|
stringBuilder.append("[optional]");
|
||||||
|
}
|
||||||
|
if (!dependency.getExclusions().isEmpty()) {
|
||||||
|
stringBuilder.append("[").append(dependency.getExclusions().size()).append(" exclusions]");
|
||||||
|
}
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean equals(Dependency d1, Dependency d2) {
|
||||||
|
return Objects.equals(d1.getGroupId(), d2.getGroupId())
|
||||||
|
&& Objects.equals(d1.getArtifactId(), d2.getArtifactId())
|
||||||
|
&& Objects.equals(d1.getVersion(), d2.getVersion())
|
||||||
|
&& Objects.equals(d1.getType(), d2.getType())
|
||||||
|
&& Objects.equals(d1.getClassifier(), d2.getClassifier())
|
||||||
|
&& Objects.equals(d1.getScope(), d2.getScope())
|
||||||
|
&& Objects.equals(d1.getSystemPath(), d2.getSystemPath())
|
||||||
|
&& Objects.equals(d1.getOptional(), d2.getOptional())
|
||||||
|
&& equals(d1.getExclusions(), d2.getExclusions());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean equals(Collection<Exclusion> ce1, Collection<Exclusion> ce2) {
|
||||||
|
if (ce1.size() == ce2.size()) {
|
||||||
|
Iterator<Exclusion> i1 = ce1.iterator();
|
||||||
|
Iterator<Exclusion> i2 = ce2.iterator();
|
||||||
|
while (i1.hasNext() && i2.hasNext()) {
|
||||||
|
if (!equals(i1.next(), i2.next())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !i1.hasNext() && !i2.hasNext();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean equals(Exclusion e1, Exclusion e2) {
|
||||||
|
return Objects.equals(e1.getGroupId(), e2.getGroupId())
|
||||||
|
&& Objects.equals(e1.getArtifactId(), e2.getArtifactId());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Dependency;
|
||||||
|
import org.apache.maven.api.model.DependencyManagement;
|
||||||
|
import org.apache.maven.api.model.Exclusion;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles injection of dependency management into the model.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"checkstyle:methodname"})
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultDependencyManagementInjector implements DependencyManagementInjector {
|
||||||
|
|
||||||
|
private ManagementModelMerger merger = new ManagementModelMerger();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model injectManagement(Model model, ModelBuilderRequest request, ModelProblemCollector problems) {
|
||||||
|
return merger.mergeManagedDependencies(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ManagementModelMerger
|
||||||
|
*/
|
||||||
|
protected static class ManagementModelMerger extends MavenModelMerger {
|
||||||
|
|
||||||
|
public Model mergeManagedDependencies(Model model) {
|
||||||
|
DependencyManagement dependencyManagement = model.getDependencyManagement();
|
||||||
|
if (dependencyManagement != null) {
|
||||||
|
Map<Object, Dependency> dependencies = new HashMap<>();
|
||||||
|
Map<Object, Object> context = Collections.emptyMap();
|
||||||
|
|
||||||
|
for (Dependency dependency : model.getDependencies()) {
|
||||||
|
Object key = getDependencyKey().apply(dependency);
|
||||||
|
dependencies.put(key, dependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean modified = false;
|
||||||
|
for (Dependency managedDependency : dependencyManagement.getDependencies()) {
|
||||||
|
Object key = getDependencyKey().apply(managedDependency);
|
||||||
|
Dependency dependency = dependencies.get(key);
|
||||||
|
if (dependency != null) {
|
||||||
|
Dependency merged = mergeDependency(dependency, managedDependency, false, context);
|
||||||
|
if (merged != dependency) {
|
||||||
|
dependencies.put(key, merged);
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modified) {
|
||||||
|
List<Dependency> newDeps = new ArrayList<>(dependencies.size());
|
||||||
|
for (Dependency dep : model.getDependencies()) {
|
||||||
|
Object key = getDependencyKey().apply(dep);
|
||||||
|
Dependency dependency = dependencies.get(key);
|
||||||
|
newDeps.add(dependency);
|
||||||
|
}
|
||||||
|
return Model.newBuilder(model).dependencies(newDeps).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeDependency_Optional(
|
||||||
|
Dependency.Builder builder,
|
||||||
|
Dependency target,
|
||||||
|
Dependency source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
// optional flag is not managed
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeDependency_Exclusions(
|
||||||
|
Dependency.Builder builder,
|
||||||
|
Dependency target,
|
||||||
|
Dependency source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<Exclusion> tgt = target.getExclusions();
|
||||||
|
if (tgt.isEmpty()) {
|
||||||
|
List<Exclusion> src = source.getExclusions();
|
||||||
|
builder.exclusions(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,330 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.InputLocation;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.ModelBase;
|
||||||
|
import org.apache.maven.api.model.Plugin;
|
||||||
|
import org.apache.maven.api.model.PluginContainer;
|
||||||
|
import org.apache.maven.api.model.ReportPlugin;
|
||||||
|
import org.apache.maven.api.model.Reporting;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles inheritance of model values.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"checkstyle:methodname"})
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultInheritanceAssembler implements InheritanceAssembler {
|
||||||
|
|
||||||
|
private static final String CHILD_DIRECTORY = "child-directory";
|
||||||
|
|
||||||
|
private static final String CHILD_DIRECTORY_PROPERTY = "project.directory";
|
||||||
|
|
||||||
|
private final InheritanceModelMerger merger = new InheritanceModelMerger();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model assembleModelInheritance(
|
||||||
|
Model child, Model parent, ModelBuilderRequest request, ModelProblemCollector problems) {
|
||||||
|
Map<Object, Object> hints = new HashMap<>();
|
||||||
|
String childPath = child.getProperties().getOrDefault(CHILD_DIRECTORY_PROPERTY, child.getArtifactId());
|
||||||
|
hints.put(CHILD_DIRECTORY, childPath);
|
||||||
|
hints.put(MavenModelMerger.CHILD_PATH_ADJUSTMENT, getChildPathAdjustment(child, parent, childPath));
|
||||||
|
return merger.merge(child, parent, false, hints);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the relative path from the base directory of the parent to the parent directory of the base directory
|
||||||
|
* of the child. The general idea is to adjust inherited URLs to match the project layout (in SCM).
|
||||||
|
*
|
||||||
|
* <p>This calculation is only a heuristic based on our conventions.
|
||||||
|
* In detail, the algo relies on the following assumptions: <ul>
|
||||||
|
* <li>The parent uses aggregation and refers to the child via the modules section</li>
|
||||||
|
* <li>The module path to the child is considered to
|
||||||
|
* point at the POM rather than its base directory if the path ends with ".xml" (ignoring case)</li>
|
||||||
|
* <li>The name of the child's base directory matches the artifact id of the child.</li>
|
||||||
|
* </ul>
|
||||||
|
* Note that for the sake of independence from the user
|
||||||
|
* environment, the filesystem is intentionally not used for the calculation.</p>
|
||||||
|
*
|
||||||
|
* @param child The child model, must not be <code>null</code>.
|
||||||
|
* @param parent The parent model, may be <code>null</code>.
|
||||||
|
* @param childDirectory The directory defined in child model, may be <code>null</code>.
|
||||||
|
* @return The path adjustment, can be empty but never <code>null</code>.
|
||||||
|
*/
|
||||||
|
private String getChildPathAdjustment(Model child, Model parent, String childDirectory) {
|
||||||
|
String adjustment = "";
|
||||||
|
|
||||||
|
if (parent != null) {
|
||||||
|
String childName = child.getArtifactId();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This logic (using filesystem, against wanted independence from the user environment) exists only for the
|
||||||
|
* sake of backward-compat with 2.x (MNG-5000). In general, it is wrong to
|
||||||
|
* base URL inheritance on the module directory names as this information is unavailable for POMs in the
|
||||||
|
* repository. In other words, modules where artifactId != moduleDirName will see different effective URLs
|
||||||
|
* depending on how the model was constructed (from filesystem or from repository).
|
||||||
|
*/
|
||||||
|
if (child.getProjectDirectory() != null) {
|
||||||
|
childName = child.getProjectDirectory().getFileName().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String module : parent.getModules()) {
|
||||||
|
module = module.replace('\\', '/');
|
||||||
|
|
||||||
|
if (module.regionMatches(true, module.length() - 4, ".xml", 0, 4)) {
|
||||||
|
module = module.substring(0, module.lastIndexOf('/') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
String moduleName = module;
|
||||||
|
if (moduleName.endsWith("/")) {
|
||||||
|
moduleName = moduleName.substring(0, moduleName.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastSlash = moduleName.lastIndexOf('/');
|
||||||
|
|
||||||
|
moduleName = moduleName.substring(lastSlash + 1);
|
||||||
|
|
||||||
|
if ((moduleName.equals(childName) || (moduleName.equals(childDirectory))) && lastSlash >= 0) {
|
||||||
|
adjustment = module.substring(0, lastSlash);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjustment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* InheritanceModelMerger
|
||||||
|
*/
|
||||||
|
protected static class InheritanceModelMerger extends MavenModelMerger {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String extrapolateChildUrl(String parentUrl, boolean appendPath, Map<Object, Object> context) {
|
||||||
|
Object childDirectory = context.get(CHILD_DIRECTORY);
|
||||||
|
Object childPathAdjustment = context.get(CHILD_PATH_ADJUSTMENT);
|
||||||
|
|
||||||
|
boolean isBlankParentUrl = true;
|
||||||
|
|
||||||
|
if (parentUrl != null) {
|
||||||
|
for (int i = 0; i < parentUrl.length(); i++) {
|
||||||
|
if (!Character.isWhitespace(parentUrl.charAt(i))) {
|
||||||
|
isBlankParentUrl = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBlankParentUrl || childDirectory == null || childPathAdjustment == null || !appendPath) {
|
||||||
|
return parentUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// append childPathAdjustment and childDirectory to parent url
|
||||||
|
return appendPath(parentUrl, childDirectory.toString(), childPathAdjustment.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String appendPath(String parentUrl, String childPath, String pathAdjustment) {
|
||||||
|
StringBuilder url = new StringBuilder(parentUrl.length()
|
||||||
|
+ pathAdjustment.length()
|
||||||
|
+ childPath.length()
|
||||||
|
+ (pathAdjustment.isEmpty() ? 1 : 2));
|
||||||
|
|
||||||
|
url.append(parentUrl);
|
||||||
|
concatPath(url, pathAdjustment);
|
||||||
|
concatPath(url, childPath);
|
||||||
|
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void concatPath(StringBuilder url, String path) {
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
boolean initialUrlEndsWithSlash = url.charAt(url.length() - 1) == '/';
|
||||||
|
boolean pathStartsWithSlash = path.charAt(0) == '/';
|
||||||
|
|
||||||
|
if (pathStartsWithSlash) {
|
||||||
|
if (initialUrlEndsWithSlash) {
|
||||||
|
// 1 extra '/' to remove
|
||||||
|
url.setLength(url.length() - 1);
|
||||||
|
}
|
||||||
|
} else if (!initialUrlEndsWithSlash) {
|
||||||
|
// add missing '/' between url and path
|
||||||
|
url.append('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
url.append(path);
|
||||||
|
|
||||||
|
// ensure resulting url ends with slash if initial url was
|
||||||
|
if (initialUrlEndsWithSlash && !path.endsWith("/")) {
|
||||||
|
url.append('/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModelBase_Properties(
|
||||||
|
ModelBase.Builder builder,
|
||||||
|
ModelBase target,
|
||||||
|
ModelBase source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
Map<String, String> merged = new HashMap<>();
|
||||||
|
if (sourceDominant) {
|
||||||
|
merged.putAll(target.getProperties());
|
||||||
|
putAll(merged, source.getProperties(), CHILD_DIRECTORY_PROPERTY);
|
||||||
|
} else {
|
||||||
|
putAll(merged, source.getProperties(), CHILD_DIRECTORY_PROPERTY);
|
||||||
|
merged.putAll(target.getProperties());
|
||||||
|
}
|
||||||
|
builder.properties(merged);
|
||||||
|
builder.location(
|
||||||
|
"properties",
|
||||||
|
InputLocation.merge(
|
||||||
|
target.getLocation("properties"), source.getLocation("properties"), sourceDominant));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putAll(Map<String, String> s, Map<String, String> t, Object excludeKey) {
|
||||||
|
for (Map.Entry<String, String> e : t.entrySet()) {
|
||||||
|
if (!e.getKey().equals(excludeKey)) {
|
||||||
|
s.put(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergePluginContainer_Plugins(
|
||||||
|
PluginContainer.Builder builder,
|
||||||
|
PluginContainer target,
|
||||||
|
PluginContainer source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<Plugin> src = source.getPlugins();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<Plugin> tgt = target.getPlugins();
|
||||||
|
Map<Object, Plugin> master = new LinkedHashMap<>(src.size() * 2);
|
||||||
|
|
||||||
|
for (Plugin element : src) {
|
||||||
|
if (element.isInherited() || !element.getExecutions().isEmpty()) {
|
||||||
|
// NOTE: Enforce recursive merge to trigger merging/inheritance logic for executions
|
||||||
|
Plugin plugin = Plugin.newInstance(false);
|
||||||
|
plugin = mergePlugin(plugin, element, sourceDominant, context);
|
||||||
|
|
||||||
|
Object key = getPluginKey().apply(plugin);
|
||||||
|
|
||||||
|
master.put(key, plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Object, List<Plugin>> predecessors = new LinkedHashMap<>();
|
||||||
|
List<Plugin> pending = new ArrayList<>();
|
||||||
|
for (Plugin element : tgt) {
|
||||||
|
Object key = getPluginKey().apply(element);
|
||||||
|
Plugin existing = master.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
element = mergePlugin(element, existing, sourceDominant, context);
|
||||||
|
|
||||||
|
master.put(key, element);
|
||||||
|
|
||||||
|
if (!pending.isEmpty()) {
|
||||||
|
predecessors.put(key, pending);
|
||||||
|
pending = new ArrayList<>();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pending.add(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Plugin> result = new ArrayList<>(src.size() + tgt.size());
|
||||||
|
for (Map.Entry<Object, Plugin> entry : master.entrySet()) {
|
||||||
|
List<Plugin> pre = predecessors.get(entry.getKey());
|
||||||
|
if (pre != null) {
|
||||||
|
result.addAll(pre);
|
||||||
|
}
|
||||||
|
result.add(entry.getValue());
|
||||||
|
}
|
||||||
|
result.addAll(pending);
|
||||||
|
|
||||||
|
builder.plugins(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Plugin mergePlugin(
|
||||||
|
Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
Plugin.Builder builder = Plugin.newBuilder(target);
|
||||||
|
if (source.isInherited()) {
|
||||||
|
mergeConfigurationContainer(builder, target, source, sourceDominant, context);
|
||||||
|
}
|
||||||
|
mergePlugin_GroupId(builder, target, source, sourceDominant, context);
|
||||||
|
mergePlugin_ArtifactId(builder, target, source, sourceDominant, context);
|
||||||
|
mergePlugin_Version(builder, target, source, sourceDominant, context);
|
||||||
|
mergePlugin_Extensions(builder, target, source, sourceDominant, context);
|
||||||
|
mergePlugin_Executions(builder, target, source, sourceDominant, context);
|
||||||
|
mergePlugin_Dependencies(builder, target, source, sourceDominant, context);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeReporting_Plugins(
|
||||||
|
Reporting.Builder builder,
|
||||||
|
Reporting target,
|
||||||
|
Reporting source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<ReportPlugin> src = source.getPlugins();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<ReportPlugin> tgt = target.getPlugins();
|
||||||
|
Map<Object, ReportPlugin> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
|
||||||
|
|
||||||
|
for (ReportPlugin element : src) {
|
||||||
|
if (element.isInherited()) {
|
||||||
|
// NOTE: Enforce recursive merge to trigger merging/inheritance logic for executions as well
|
||||||
|
ReportPlugin plugin = ReportPlugin.newInstance(false);
|
||||||
|
plugin = mergeReportPlugin(plugin, element, sourceDominant, context);
|
||||||
|
|
||||||
|
merged.put(getReportPluginKey().apply(element), plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ReportPlugin element : tgt) {
|
||||||
|
Object key = getReportPluginKey().apply(element);
|
||||||
|
ReportPlugin existing = merged.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
element = mergeReportPlugin(element, existing, sourceDominant, context);
|
||||||
|
}
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.plugins(merged.values());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.apache.maven.api.Packaging;
|
||||||
|
import org.apache.maven.api.di.Inject;
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Build;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.Plugin;
|
||||||
|
import org.apache.maven.api.model.PluginContainer;
|
||||||
|
import org.apache.maven.api.model.PluginExecution;
|
||||||
|
import org.apache.maven.api.model.PluginManagement;
|
||||||
|
import org.apache.maven.api.services.BuilderProblem.Severity;
|
||||||
|
import org.apache.maven.api.services.LifecycleRegistry;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblem.Version;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.PackagingRegistry;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles injection of plugin executions induced by the lifecycle bindings for a packaging.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultLifecycleBindingsInjector implements LifecycleBindingsInjector {
|
||||||
|
|
||||||
|
private final LifecycleBindingsMerger merger = new LifecycleBindingsMerger();
|
||||||
|
|
||||||
|
private final LifecycleRegistry lifecycleRegistry;
|
||||||
|
private final PackagingRegistry packagingRegistry;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DefaultLifecycleBindingsInjector(LifecycleRegistry lifecycleRegistry, PackagingRegistry packagingRegistry) {
|
||||||
|
this.lifecycleRegistry = lifecycleRegistry;
|
||||||
|
this.packagingRegistry = packagingRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Model injectLifecycleBindings(Model model, ModelBuilderRequest request, ModelProblemCollector problems) {
|
||||||
|
String packagingId = model.getPackaging();
|
||||||
|
Packaging packaging = packagingRegistry.lookup(packagingId).orElse(null);
|
||||||
|
if (packaging == null) {
|
||||||
|
problems.add(
|
||||||
|
Severity.ERROR, Version.BASE, "Unknown packaging: " + packagingId, model.getLocation("packaging"));
|
||||||
|
return model;
|
||||||
|
} else {
|
||||||
|
Map<String, PluginContainer> plugins = new HashMap<>(packaging.plugins());
|
||||||
|
lifecycleRegistry.stream()
|
||||||
|
.filter(lf -> !plugins.containsKey(lf.id()))
|
||||||
|
.forEach(lf -> plugins.put(
|
||||||
|
lf.id(),
|
||||||
|
PluginContainer.newBuilder()
|
||||||
|
.plugins(lf.phases().stream()
|
||||||
|
.flatMap(phase -> phase.plugins().stream())
|
||||||
|
.toList())
|
||||||
|
.build()));
|
||||||
|
Map<Plugin, Plugin> allPlugins = new LinkedHashMap<>();
|
||||||
|
plugins.values().stream().flatMap(pc -> pc.getPlugins().stream()).forEach(p -> addPlugin(allPlugins, p));
|
||||||
|
Model lifecycleModel = Model.newBuilder()
|
||||||
|
.build(Build.newBuilder().plugins(allPlugins.values()).build())
|
||||||
|
.build();
|
||||||
|
return merger.merge(model, lifecycleModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPlugin(Map<Plugin, Plugin> plugins, Plugin plugin) {
|
||||||
|
Plugin cur = plugins.putIfAbsent(plugin, plugin);
|
||||||
|
if (cur != null) {
|
||||||
|
Map<String, PluginExecution> execs = new LinkedHashMap<>();
|
||||||
|
cur.getExecutions().forEach(e -> execs.put(e.getId(), e));
|
||||||
|
plugin.getExecutions().forEach(e -> {
|
||||||
|
int i = 0;
|
||||||
|
String id = e.getId();
|
||||||
|
while (execs.putIfAbsent(id, e.withId(id)) != null) {
|
||||||
|
id = e.getId() + "-" + (++i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Plugin merged = cur.withExecutions(execs.values());
|
||||||
|
plugins.put(merged, merged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getExecutionId(Plugin plugin, String goal) {
|
||||||
|
Set<String> existingIds = plugin != null
|
||||||
|
? plugin.getExecutions().stream().map(PluginExecution::getId).collect(Collectors.toSet())
|
||||||
|
: Set.of();
|
||||||
|
String base = "default-" + goal;
|
||||||
|
String id = base;
|
||||||
|
for (int index = 1; existingIds.contains(id); index++) {
|
||||||
|
id = base + '-' + index;
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The domain-specific model merger for lifecycle bindings
|
||||||
|
*/
|
||||||
|
protected static class LifecycleBindingsMerger extends MavenModelMerger {
|
||||||
|
|
||||||
|
private static final String PLUGIN_MANAGEMENT = "plugin-management";
|
||||||
|
|
||||||
|
public Model merge(Model target, Model source) {
|
||||||
|
Build targetBuild = target.getBuild();
|
||||||
|
if (targetBuild == null) {
|
||||||
|
targetBuild = Build.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Object, Object> context =
|
||||||
|
Collections.singletonMap(PLUGIN_MANAGEMENT, targetBuild.getPluginManagement());
|
||||||
|
|
||||||
|
Build.Builder builder = Build.newBuilder(targetBuild);
|
||||||
|
mergePluginContainer_Plugins(builder, targetBuild, source.getBuild(), false, context);
|
||||||
|
|
||||||
|
return target.withBuild(builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"checkstyle:methodname"})
|
||||||
|
@Override
|
||||||
|
protected void mergePluginContainer_Plugins(
|
||||||
|
PluginContainer.Builder builder,
|
||||||
|
PluginContainer target,
|
||||||
|
PluginContainer source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<Plugin> src = source.getPlugins();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<Plugin> tgt = target.getPlugins();
|
||||||
|
|
||||||
|
Map<Object, Plugin> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
|
||||||
|
|
||||||
|
for (Plugin element : tgt) {
|
||||||
|
Object key = getPluginKey().apply(element);
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Object, Plugin> added = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
for (Plugin element : src) {
|
||||||
|
Object key = getPluginKey().apply(element);
|
||||||
|
Plugin existing = merged.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
element = mergePlugin(existing, element, sourceDominant, context);
|
||||||
|
} else {
|
||||||
|
added.put(key, element);
|
||||||
|
}
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!added.isEmpty()) {
|
||||||
|
PluginManagement pluginMgmt = (PluginManagement) context.get(PLUGIN_MANAGEMENT);
|
||||||
|
if (pluginMgmt != null) {
|
||||||
|
for (Plugin managedPlugin : pluginMgmt.getPlugins()) {
|
||||||
|
Object key = getPluginKey().apply(managedPlugin);
|
||||||
|
Plugin addedPlugin = added.get(key);
|
||||||
|
if (addedPlugin != null) {
|
||||||
|
Plugin plugin =
|
||||||
|
mergePlugin(managedPlugin, addedPlugin, sourceDominant, Collections.emptyMap());
|
||||||
|
merged.put(key, plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Plugin> result = new ArrayList<>(merged.values());
|
||||||
|
|
||||||
|
builder.plugins(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergePluginExecution_Priority(
|
||||||
|
PluginExecution.Builder builder,
|
||||||
|
PluginExecution target,
|
||||||
|
PluginExecution source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
if (target.getPriority() > source.getPriority()) {
|
||||||
|
builder.priority(source.getPriority());
|
||||||
|
builder.location("priority", source.getLocation("priority"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// mergePluginExecution_Priority( builder, target, source, sourceDominant, context );
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,222 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderResult;
|
||||||
|
import org.apache.maven.api.services.ModelProblem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects the output of the model builder.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class DefaultModelBuilderResult implements ModelBuilderResult {
|
||||||
|
private Model fileModel;
|
||||||
|
private Model activatedFileModel;
|
||||||
|
|
||||||
|
private Model effectiveModel;
|
||||||
|
|
||||||
|
private List<String> modelIds;
|
||||||
|
|
||||||
|
private Map<String, Model> rawModels;
|
||||||
|
|
||||||
|
private Map<String, List<Profile>> activePomProfiles;
|
||||||
|
|
||||||
|
private List<Profile> activeExternalProfiles;
|
||||||
|
|
||||||
|
private List<ModelProblem> problems;
|
||||||
|
|
||||||
|
DefaultModelBuilderResult() {
|
||||||
|
modelIds = new ArrayList<>();
|
||||||
|
rawModels = new HashMap<>();
|
||||||
|
activePomProfiles = new HashMap<>();
|
||||||
|
activeExternalProfiles = new ArrayList<>();
|
||||||
|
problems = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultModelBuilderResult(ModelBuilderResult result) {
|
||||||
|
this();
|
||||||
|
this.activeExternalProfiles.addAll(result.getActiveExternalProfiles());
|
||||||
|
this.effectiveModel = result.getEffectiveModel();
|
||||||
|
this.fileModel = result.getFileModel();
|
||||||
|
this.problems.addAll(result.getProblems());
|
||||||
|
|
||||||
|
for (String modelId : result.getModelIds()) {
|
||||||
|
this.modelIds.add(modelId);
|
||||||
|
this.rawModels.put(modelId, result.getRawModel(modelId).orElseThrow());
|
||||||
|
this.activePomProfiles.put(modelId, result.getActivePomProfiles(modelId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model getFileModel() {
|
||||||
|
return fileModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultModelBuilderResult setFileModel(Model fileModel) {
|
||||||
|
this.fileModel = fileModel;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Model getActivatedFileModel() {
|
||||||
|
return activatedFileModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultModelBuilderResult setActivatedFileModel(Model activatedFileModel) {
|
||||||
|
this.activatedFileModel = activatedFileModel;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model getEffectiveModel() {
|
||||||
|
return effectiveModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultModelBuilderResult setEffectiveModel(Model model) {
|
||||||
|
this.effectiveModel = model;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getModelIds() {
|
||||||
|
return modelIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultModelBuilderResult addModelId(String modelId) {
|
||||||
|
// Intentionally notNull because Super POM may not contain a modelId
|
||||||
|
Objects.requireNonNull(modelId, "modelId cannot be null");
|
||||||
|
|
||||||
|
modelIds.add(modelId);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model getRawModel() {
|
||||||
|
return rawModels.get(modelIds.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Model> getRawModel(String modelId) {
|
||||||
|
return Optional.ofNullable(rawModels.get(modelId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultModelBuilderResult setRawModel(String modelId, Model rawModel) {
|
||||||
|
// Intentionally notNull because Super POM may not contain a modelId
|
||||||
|
Objects.requireNonNull(modelId, "modelId cannot be null");
|
||||||
|
|
||||||
|
rawModels.put(modelId, rawModel);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Profile> getActivePomProfiles(String modelId) {
|
||||||
|
List<Profile> profiles = activePomProfiles.get(modelId);
|
||||||
|
return profiles != null ? profiles : List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultModelBuilderResult setActivePomProfiles(String modelId, List<Profile> activeProfiles) {
|
||||||
|
// Intentionally notNull because Super POM may not contain a modelId
|
||||||
|
Objects.requireNonNull(modelId, "modelId cannot be null");
|
||||||
|
|
||||||
|
if (activeProfiles != null) {
|
||||||
|
this.activePomProfiles.put(modelId, new ArrayList<>(activeProfiles));
|
||||||
|
} else {
|
||||||
|
this.activePomProfiles.remove(modelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Profile> getActiveExternalProfiles() {
|
||||||
|
return activeExternalProfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultModelBuilderResult setActiveExternalProfiles(List<Profile> activeProfiles) {
|
||||||
|
if (activeProfiles != null) {
|
||||||
|
this.activeExternalProfiles = new ArrayList<>(activeProfiles);
|
||||||
|
} else {
|
||||||
|
this.activeExternalProfiles.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ModelProblem> getProblems() {
|
||||||
|
return problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultModelBuilderResult setProblems(List<ModelProblem> problems) {
|
||||||
|
if (problems != null) {
|
||||||
|
this.problems = new ArrayList<>(problems);
|
||||||
|
} else {
|
||||||
|
this.problems.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
if (!modelIds.isEmpty()) {
|
||||||
|
String modelId = modelIds.get(0);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(problems.size())
|
||||||
|
.append(
|
||||||
|
(problems.size() == 1)
|
||||||
|
? " problem was "
|
||||||
|
: " problems were encountered while building the effective model");
|
||||||
|
if (modelId != null && !modelId.isEmpty()) {
|
||||||
|
sb.append(" for ");
|
||||||
|
sb.append(modelId);
|
||||||
|
}
|
||||||
|
for (ModelProblem problem : problems) {
|
||||||
|
sb.append(System.lineSeparator());
|
||||||
|
sb.append(" - [");
|
||||||
|
sb.append(problem.getSeverity());
|
||||||
|
sb.append("] ");
|
||||||
|
sb.append(problem.getMessage());
|
||||||
|
String loc = Stream.of(
|
||||||
|
problem.getModelId().equals(modelId) ? problem.getModelId() : "",
|
||||||
|
problem.getModelId().equals(modelId) ? problem.getSource() : "",
|
||||||
|
problem.getLineNumber() > 0 ? "line " + problem.getLineNumber() : "",
|
||||||
|
problem.getColumnNumber() > 0 ? "column " + problem.getColumnNumber() : "")
|
||||||
|
.filter(s -> !s.isEmpty())
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
if (!loc.isEmpty()) {
|
||||||
|
sb.append(" @ ").append(loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.ModelBuildingEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds data relevant for a model building event.
|
||||||
|
*/
|
||||||
|
record DefaultModelBuildingEvent(
|
||||||
|
Model model, Consumer<Model> update, ModelBuilderRequest request, ModelProblemCollector problems)
|
||||||
|
implements ModelBuildingEvent {}
|
|
@ -0,0 +1,485 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Inject;
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.BuilderProblem;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblem;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
import org.apache.maven.model.v4.MavenTransformer;
|
||||||
|
import org.codehaus.plexus.interpolation.AbstractDelegatingValueSource;
|
||||||
|
import org.codehaus.plexus.interpolation.AbstractValueSource;
|
||||||
|
import org.codehaus.plexus.interpolation.InterpolationException;
|
||||||
|
import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
|
||||||
|
import org.codehaus.plexus.interpolation.MapBasedValueSource;
|
||||||
|
import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
|
||||||
|
import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
|
||||||
|
import org.codehaus.plexus.interpolation.QueryEnabledValueSource;
|
||||||
|
import org.codehaus.plexus.interpolation.RecursionInterceptor;
|
||||||
|
import org.codehaus.plexus.interpolation.StringSearchInterpolator;
|
||||||
|
import org.codehaus.plexus.interpolation.ValueSource;
|
||||||
|
import org.codehaus.plexus.interpolation.reflection.ReflectionValueExtractor;
|
||||||
|
import org.codehaus.plexus.interpolation.util.ValueSourceUtils;
|
||||||
|
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultModelInterpolator implements ModelInterpolator {
|
||||||
|
|
||||||
|
private static final String PREFIX_PROJECT = "project.";
|
||||||
|
private static final String PREFIX_POM = "pom.";
|
||||||
|
private static final List<String> PROJECT_PREFIXES_3_1 = Arrays.asList(PREFIX_POM, PREFIX_PROJECT);
|
||||||
|
private static final List<String> PROJECT_PREFIXES_4_0 = Collections.singletonList(PREFIX_PROJECT);
|
||||||
|
|
||||||
|
private static final Collection<String> TRANSLATED_PATH_EXPRESSIONS;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Collection<String> translatedPrefixes = new HashSet<>();
|
||||||
|
|
||||||
|
// MNG-1927, MNG-2124, MNG-3355:
|
||||||
|
// If the build section is present and the project directory is non-null, we should make
|
||||||
|
// sure interpolation of the directories below uses translated paths.
|
||||||
|
// Afterward, we'll double back and translate any paths that weren't covered during interpolation via the
|
||||||
|
// code below...
|
||||||
|
translatedPrefixes.add("build.directory");
|
||||||
|
translatedPrefixes.add("build.outputDirectory");
|
||||||
|
translatedPrefixes.add("build.testOutputDirectory");
|
||||||
|
translatedPrefixes.add("build.sourceDirectory");
|
||||||
|
translatedPrefixes.add("build.testSourceDirectory");
|
||||||
|
translatedPrefixes.add("build.scriptSourceDirectory");
|
||||||
|
translatedPrefixes.add("reporting.outputDirectory");
|
||||||
|
|
||||||
|
TRANSLATED_PATH_EXPRESSIONS = translatedPrefixes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final PathTranslator pathTranslator;
|
||||||
|
private final UrlNormalizer urlNormalizer;
|
||||||
|
private final RootLocator rootLocator;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DefaultModelInterpolator(
|
||||||
|
PathTranslator pathTranslator, UrlNormalizer urlNormalizer, RootLocator rootLocator) {
|
||||||
|
this.pathTranslator = pathTranslator;
|
||||||
|
this.urlNormalizer = urlNormalizer;
|
||||||
|
this.rootLocator = rootLocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InnerInterpolator {
|
||||||
|
String interpolate(String value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model interpolateModel(
|
||||||
|
Model model, Path projectDir, ModelBuilderRequest request, ModelProblemCollector problems) {
|
||||||
|
List<? extends ValueSource> valueSources = createValueSources(model, projectDir, request, problems);
|
||||||
|
List<? extends InterpolationPostProcessor> postProcessors = createPostProcessors(model, projectDir, request);
|
||||||
|
|
||||||
|
InnerInterpolator innerInterpolator = createInterpolator(valueSources, postProcessors, request, problems);
|
||||||
|
|
||||||
|
return new MavenTransformer(innerInterpolator::interpolate).visit(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InnerInterpolator createInterpolator(
|
||||||
|
List<? extends ValueSource> valueSources,
|
||||||
|
List<? extends InterpolationPostProcessor> postProcessors,
|
||||||
|
ModelBuilderRequest request,
|
||||||
|
ModelProblemCollector problems) {
|
||||||
|
Map<String, String> cache = new HashMap<>();
|
||||||
|
StringSearchInterpolator interpolator = new StringSearchInterpolator();
|
||||||
|
interpolator.setCacheAnswers(true);
|
||||||
|
for (ValueSource vs : valueSources) {
|
||||||
|
interpolator.addValueSource(vs);
|
||||||
|
}
|
||||||
|
for (InterpolationPostProcessor postProcessor : postProcessors) {
|
||||||
|
interpolator.addPostProcessor(postProcessor);
|
||||||
|
}
|
||||||
|
RecursionInterceptor recursionInterceptor = createRecursionInterceptor(request);
|
||||||
|
return value -> {
|
||||||
|
if (value != null && value.contains("${")) {
|
||||||
|
String c = cache.get(value);
|
||||||
|
if (c == null) {
|
||||||
|
try {
|
||||||
|
c = interpolator.interpolate(value, recursionInterceptor);
|
||||||
|
} catch (InterpolationException e) {
|
||||||
|
problems.add(BuilderProblem.Severity.ERROR, ModelProblem.Version.BASE, e.getMessage(), e);
|
||||||
|
}
|
||||||
|
cache.put(value, c);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<String> getProjectPrefixes(ModelBuilderRequest request) {
|
||||||
|
return request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_4_0
|
||||||
|
? PROJECT_PREFIXES_4_0
|
||||||
|
: PROJECT_PREFIXES_3_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<ValueSource> createValueSources(
|
||||||
|
Model model, Path projectDir, ModelBuilderRequest request, ModelProblemCollector problems) {
|
||||||
|
Map<String, String> modelProperties = model.getProperties();
|
||||||
|
|
||||||
|
ValueSource projectPrefixValueSource;
|
||||||
|
ValueSource prefixlessObjectBasedValueSource;
|
||||||
|
if (request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_4_0) {
|
||||||
|
projectPrefixValueSource = new PrefixedObjectValueSource(PROJECT_PREFIXES_4_0, model, false);
|
||||||
|
prefixlessObjectBasedValueSource = new ObjectBasedValueSource(model);
|
||||||
|
} else {
|
||||||
|
projectPrefixValueSource = new PrefixedObjectValueSource(PROJECT_PREFIXES_3_1, model, false);
|
||||||
|
if (request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0) {
|
||||||
|
projectPrefixValueSource =
|
||||||
|
new ProblemDetectingValueSource(projectPrefixValueSource, PREFIX_POM, PREFIX_PROJECT, problems);
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixlessObjectBasedValueSource = new ObjectBasedValueSource(model);
|
||||||
|
if (request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0) {
|
||||||
|
prefixlessObjectBasedValueSource =
|
||||||
|
new ProblemDetectingValueSource(prefixlessObjectBasedValueSource, "", PREFIX_PROJECT, problems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Order counts here!
|
||||||
|
List<ValueSource> valueSources = new ArrayList<>(9);
|
||||||
|
|
||||||
|
if (projectDir != null) {
|
||||||
|
ValueSource basedirValueSource = new PrefixedValueSourceWrapper(
|
||||||
|
new AbstractValueSource(false) {
|
||||||
|
@Override
|
||||||
|
public Object getValue(String expression) {
|
||||||
|
if ("basedir".equals(expression)) {
|
||||||
|
return projectDir.toAbsolutePath().toString();
|
||||||
|
} else if (expression.startsWith("basedir.")) {
|
||||||
|
Path basedir = projectDir.toAbsolutePath();
|
||||||
|
return new ObjectBasedValueSource(basedir)
|
||||||
|
.getValue(expression.substring("basedir.".length()));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getProjectPrefixes(request),
|
||||||
|
true);
|
||||||
|
valueSources.add(basedirValueSource);
|
||||||
|
|
||||||
|
ValueSource baseUriValueSource = new PrefixedValueSourceWrapper(
|
||||||
|
new AbstractValueSource(false) {
|
||||||
|
@Override
|
||||||
|
public Object getValue(String expression) {
|
||||||
|
if ("baseUri".equals(expression)) {
|
||||||
|
return projectDir.toAbsolutePath().toUri().toASCIIString();
|
||||||
|
} else if (expression.startsWith("baseUri.")) {
|
||||||
|
URI baseUri = projectDir.toAbsolutePath().toUri();
|
||||||
|
return new ObjectBasedValueSource(baseUri)
|
||||||
|
.getValue(expression.substring("baseUri.".length()));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getProjectPrefixes(request),
|
||||||
|
false);
|
||||||
|
valueSources.add(baseUriValueSource);
|
||||||
|
valueSources.add(new BuildTimestampValueSource(request.getSession().getStartTime(), modelProperties));
|
||||||
|
}
|
||||||
|
|
||||||
|
valueSources.add(new PrefixedValueSourceWrapper(
|
||||||
|
new AbstractValueSource(false) {
|
||||||
|
@Override
|
||||||
|
public Object getValue(String expression) {
|
||||||
|
if ("rootDirectory".equals(expression)) {
|
||||||
|
Path root = rootLocator.findMandatoryRoot(projectDir);
|
||||||
|
return root.toFile().getPath();
|
||||||
|
} else if (expression.startsWith("rootDirectory.")) {
|
||||||
|
Path root = rootLocator.findMandatoryRoot(projectDir);
|
||||||
|
return new ObjectBasedValueSource(root)
|
||||||
|
.getValue(expression.substring("rootDirectory.".length()));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getProjectPrefixes(request)));
|
||||||
|
|
||||||
|
valueSources.add(projectPrefixValueSource);
|
||||||
|
|
||||||
|
valueSources.add(new MapBasedValueSource(request.getUserProperties()));
|
||||||
|
|
||||||
|
valueSources.add(new MapBasedValueSource(modelProperties));
|
||||||
|
|
||||||
|
valueSources.add(new MapBasedValueSource(request.getSystemProperties()));
|
||||||
|
|
||||||
|
valueSources.add(new AbstractValueSource(false) {
|
||||||
|
@Override
|
||||||
|
public Object getValue(String expression) {
|
||||||
|
return request.getSystemProperties().get("env." + expression);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
valueSources.add(prefixlessObjectBasedValueSource);
|
||||||
|
|
||||||
|
return valueSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<? extends InterpolationPostProcessor> createPostProcessors(
|
||||||
|
Model model, Path projectDir, ModelBuilderRequest request) {
|
||||||
|
List<InterpolationPostProcessor> processors = new ArrayList<>(2);
|
||||||
|
if (projectDir != null) {
|
||||||
|
processors.add(new PathTranslatingPostProcessor(
|
||||||
|
getProjectPrefixes(request), TRANSLATED_PATH_EXPRESSIONS, projectDir, pathTranslator));
|
||||||
|
}
|
||||||
|
processors.add(new UrlNormalizingPostProcessor(urlNormalizer));
|
||||||
|
return processors;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RecursionInterceptor createRecursionInterceptor(ModelBuilderRequest request) {
|
||||||
|
return new PrefixAwareRecursionInterceptor(getProjectPrefixes(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PathTranslatingPostProcessor implements InterpolationPostProcessor {
|
||||||
|
|
||||||
|
private final Collection<String> unprefixedPathKeys;
|
||||||
|
private final Path projectDir;
|
||||||
|
private final PathTranslator pathTranslator;
|
||||||
|
private final List<String> expressionPrefixes;
|
||||||
|
|
||||||
|
PathTranslatingPostProcessor(
|
||||||
|
List<String> expressionPrefixes,
|
||||||
|
Collection<String> unprefixedPathKeys,
|
||||||
|
Path projectDir,
|
||||||
|
PathTranslator pathTranslator) {
|
||||||
|
this.expressionPrefixes = expressionPrefixes;
|
||||||
|
this.unprefixedPathKeys = unprefixedPathKeys;
|
||||||
|
this.projectDir = projectDir;
|
||||||
|
this.pathTranslator = pathTranslator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object execute(String expression, Object value) {
|
||||||
|
if (value != null) {
|
||||||
|
expression = ValueSourceUtils.trimPrefix(expression, expressionPrefixes, true);
|
||||||
|
if (unprefixedPathKeys.contains(expression)) {
|
||||||
|
return pathTranslator.alignToBaseDirectory(String.valueOf(value), projectDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that expressions referring to URLs evaluate to normalized URLs.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static class UrlNormalizingPostProcessor implements InterpolationPostProcessor {
|
||||||
|
|
||||||
|
private static final Set<String> URL_EXPRESSIONS;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Set<String> expressions = new HashSet<>();
|
||||||
|
expressions.add("project.url");
|
||||||
|
expressions.add("project.scm.url");
|
||||||
|
expressions.add("project.scm.connection");
|
||||||
|
expressions.add("project.scm.developerConnection");
|
||||||
|
expressions.add("project.distributionManagement.site.url");
|
||||||
|
URL_EXPRESSIONS = expressions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final UrlNormalizer normalizer;
|
||||||
|
|
||||||
|
UrlNormalizingPostProcessor(UrlNormalizer normalizer) {
|
||||||
|
this.normalizer = normalizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object execute(String expression, Object value) {
|
||||||
|
if (value != null && URL_EXPRESSIONS.contains(expression)) {
|
||||||
|
return normalizer.normalize(value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps an arbitrary object with an {@link ObjectBasedValueSource} instance, then
|
||||||
|
* wraps that source with a {@link PrefixedValueSourceWrapper} instance, to which
|
||||||
|
* this class delegates all of its calls.
|
||||||
|
*/
|
||||||
|
public static class PrefixedObjectValueSource extends AbstractDelegatingValueSource
|
||||||
|
implements QueryEnabledValueSource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap the specified root object, allowing the specified expression prefix.
|
||||||
|
* @param prefix the prefix.
|
||||||
|
* @param root the root of the graph.
|
||||||
|
*/
|
||||||
|
public PrefixedObjectValueSource(String prefix, Object root) {
|
||||||
|
super(new PrefixedValueSourceWrapper(new ObjectBasedValueSource(root), prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap the specified root object, allowing the specified list of expression
|
||||||
|
* prefixes and setting whether the {@link PrefixedValueSourceWrapper} allows
|
||||||
|
* unprefixed expressions.
|
||||||
|
* @param possiblePrefixes The possible prefixes.
|
||||||
|
* @param root The root of the graph.
|
||||||
|
* @param allowUnprefixedExpressions if we allow undefined expressions or not.
|
||||||
|
*/
|
||||||
|
public PrefixedObjectValueSource(
|
||||||
|
List<String> possiblePrefixes, Object root, boolean allowUnprefixedExpressions) {
|
||||||
|
super(new PrefixedValueSourceWrapper(
|
||||||
|
new ObjectBasedValueSource(root), possiblePrefixes, allowUnprefixedExpressions));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public String getLastExpression() {
|
||||||
|
return ((QueryEnabledValueSource) getDelegate()).getLastExpression();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps an object, providing reflective access to the object graph of which the
|
||||||
|
* supplied object is the root. Expressions like 'child.name' will translate into
|
||||||
|
* 'rootObject.getChild().getName()' for non-boolean properties, and
|
||||||
|
* 'rootObject.getChild().isName()' for boolean properties.
|
||||||
|
*/
|
||||||
|
public static class ObjectBasedValueSource extends AbstractValueSource {
|
||||||
|
|
||||||
|
private final Object root;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new value source, using the supplied object as the root from
|
||||||
|
* which to start, and using expressions split at the dot ('.') to navigate
|
||||||
|
* the object graph beneath this root.
|
||||||
|
* @param root the root of the graph.
|
||||||
|
*/
|
||||||
|
public ObjectBasedValueSource(Object root) {
|
||||||
|
super(true);
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Split the expression into parts, tokenized on the dot ('.') character. Then,
|
||||||
|
* starting at the root object contained in this value source, apply each part
|
||||||
|
* to the object graph below this root, using either 'getXXX()' or 'isXXX()'
|
||||||
|
* accessor types to resolve the value for each successive expression part.
|
||||||
|
* Finally, return the result of the last expression part's resolution.</p>
|
||||||
|
*
|
||||||
|
* <p><b>NOTE:</b> The object-graph nagivation actually takes place via the
|
||||||
|
* {@link ReflectionValueExtractor} class.</p>
|
||||||
|
*/
|
||||||
|
public Object getValue(String expression) {
|
||||||
|
if (expression == null || expression.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return ReflectionValueExtractor.evaluate(expression, root, false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
addFeedback("Failed to extract \'" + expression + "\' from: " + root, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps another value source and intercepts interpolated expressions, checking for problems.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static class ProblemDetectingValueSource implements ValueSource {
|
||||||
|
|
||||||
|
private final ValueSource valueSource;
|
||||||
|
|
||||||
|
private final String bannedPrefix;
|
||||||
|
|
||||||
|
private final String newPrefix;
|
||||||
|
|
||||||
|
private final ModelProblemCollector problems;
|
||||||
|
|
||||||
|
ProblemDetectingValueSource(
|
||||||
|
ValueSource valueSource, String bannedPrefix, String newPrefix, ModelProblemCollector problems) {
|
||||||
|
this.valueSource = valueSource;
|
||||||
|
this.bannedPrefix = bannedPrefix;
|
||||||
|
this.newPrefix = newPrefix;
|
||||||
|
this.problems = problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue(String expression) {
|
||||||
|
Object value = valueSource.getValue(expression);
|
||||||
|
|
||||||
|
if (value != null && expression.startsWith(bannedPrefix)) {
|
||||||
|
String msg = "The expression ${" + expression + "} is deprecated.";
|
||||||
|
if (newPrefix != null && !newPrefix.isEmpty()) {
|
||||||
|
msg += " Please use ${" + newPrefix + expression.substring(bannedPrefix.length()) + "} instead.";
|
||||||
|
}
|
||||||
|
problems.add(BuilderProblem.Severity.WARNING, ModelProblem.Version.V20, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List getFeedback() {
|
||||||
|
return valueSource.getFeedback();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearFeedback() {
|
||||||
|
valueSource.clearFeedback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BuildTimestampValueSource extends AbstractValueSource {
|
||||||
|
private final Instant startTime;
|
||||||
|
private final Map<String, String> properties;
|
||||||
|
|
||||||
|
BuildTimestampValueSource(Instant startTime, Map<String, String> properties) {
|
||||||
|
super(false);
|
||||||
|
this.startTime = startTime;
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue(String expression) {
|
||||||
|
if ("build.timestamp".equals(expression) || "maven.build.timestamp".equals(expression)) {
|
||||||
|
return new MavenBuildTimestamp(startTime, properties).formattedTimestamp();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Build;
|
||||||
|
import org.apache.maven.api.model.Dependency;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.Plugin;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles normalization of a model.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultModelNormalizer implements ModelNormalizer {
|
||||||
|
|
||||||
|
private DuplicateMerger merger = new DuplicateMerger();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model mergeDuplicates(Model model, ModelBuilderRequest request, ModelProblemCollector problems) {
|
||||||
|
Model.Builder builder = Model.newBuilder(model);
|
||||||
|
|
||||||
|
Build build = model.getBuild();
|
||||||
|
if (build != null) {
|
||||||
|
List<Plugin> plugins = build.getPlugins();
|
||||||
|
Map<Object, Plugin> normalized = new LinkedHashMap<>(plugins.size() * 2);
|
||||||
|
|
||||||
|
for (Plugin plugin : plugins) {
|
||||||
|
Object key = plugin.getKey();
|
||||||
|
Plugin first = normalized.get(key);
|
||||||
|
if (first != null) {
|
||||||
|
plugin = merger.mergePlugin(plugin, first);
|
||||||
|
}
|
||||||
|
normalized.put(key, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plugins.size() != normalized.size()) {
|
||||||
|
builder.build(
|
||||||
|
Build.newBuilder(build).plugins(normalized.values()).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: This is primarily to keep backward-compat with Maven 2.x which did not validate that dependencies are
|
||||||
|
* unique within a single POM. Upon multiple declarations, 2.x just kept the last one but retained the order of
|
||||||
|
* the first occurrence. So when we're in lenient/compat mode, we have to deal with such broken POMs and mimic
|
||||||
|
* the way 2.x works. When we're in strict mode, the removal of duplicates just saves other merging steps from
|
||||||
|
* aftereffects and bogus error messages.
|
||||||
|
*/
|
||||||
|
List<Dependency> dependencies = model.getDependencies();
|
||||||
|
Map<String, Dependency> normalized = new LinkedHashMap<>(dependencies.size() * 2);
|
||||||
|
|
||||||
|
for (Dependency dependency : dependencies) {
|
||||||
|
normalized.put(dependency.getManagementKey(), dependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dependencies.size() != normalized.size()) {
|
||||||
|
builder.dependencies(normalized.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DuplicateMerger
|
||||||
|
*/
|
||||||
|
protected static class DuplicateMerger extends MavenModelMerger {
|
||||||
|
|
||||||
|
public Plugin mergePlugin(Plugin target, Plugin source) {
|
||||||
|
return super.mergePlugin(target, source, false, Collections.emptyMap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model injectDefaultValues(Model model, ModelBuilderRequest request, ModelProblemCollector problems) {
|
||||||
|
Model.Builder builder = Model.newBuilder(model);
|
||||||
|
|
||||||
|
builder.dependencies(injectList(model.getDependencies(), this::injectDependency));
|
||||||
|
Build build = model.getBuild();
|
||||||
|
if (build != null) {
|
||||||
|
Build newBuild = Build.newBuilder(build)
|
||||||
|
.plugins(injectList(build.getPlugins(), this::injectPlugin))
|
||||||
|
.build();
|
||||||
|
builder.build(newBuild != build ? newBuild : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Plugin injectPlugin(Plugin p) {
|
||||||
|
return Plugin.newBuilder(p)
|
||||||
|
.dependencies(injectList(p.getDependencies(), this::injectDependency))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dependency injectDependency(Dependency d) {
|
||||||
|
// we cannot set this directly in the MDO due to the interactions with dependency management
|
||||||
|
return (d.getScope() == null || d.getScope().isEmpty()) ? d.withScope("compile") : d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list suited for the builders, i.e. null if not modified
|
||||||
|
*/
|
||||||
|
private <T> List<T> injectList(List<T> list, Function<T, T> modifer) {
|
||||||
|
List<T> newList = null;
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
T oldT = list.get(i);
|
||||||
|
T newT = modifer.apply(oldT);
|
||||||
|
if (newT != oldT) {
|
||||||
|
if (newList == null) {
|
||||||
|
newList = new ArrayList<>(list);
|
||||||
|
}
|
||||||
|
newList.set(i, newT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newList;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Inject;
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Build;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.Reporting;
|
||||||
|
import org.apache.maven.api.model.Resource;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves relative paths within a model against a specific base directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultModelPathTranslator implements ModelPathTranslator {
|
||||||
|
|
||||||
|
private final PathTranslator pathTranslator;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DefaultModelPathTranslator(PathTranslator pathTranslator) {
|
||||||
|
this.pathTranslator = pathTranslator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model alignToBaseDirectory(Model model, Path basedir, ModelBuilderRequest request) {
|
||||||
|
if (model == null || basedir == null) {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
Build build = model.getBuild();
|
||||||
|
Build newBuild = null;
|
||||||
|
if (build != null) {
|
||||||
|
newBuild = Build.newBuilder(build)
|
||||||
|
.directory(alignToBaseDirectory(build.getDirectory(), basedir))
|
||||||
|
.sourceDirectory(alignToBaseDirectory(build.getSourceDirectory(), basedir))
|
||||||
|
.testSourceDirectory(alignToBaseDirectory(build.getTestSourceDirectory(), basedir))
|
||||||
|
.scriptSourceDirectory(alignToBaseDirectory(build.getScriptSourceDirectory(), basedir))
|
||||||
|
.resources(map(build.getResources(), r -> alignToBaseDirectory(r, basedir)))
|
||||||
|
.testResources(map(build.getTestResources(), r -> alignToBaseDirectory(r, basedir)))
|
||||||
|
.filters(map(build.getFilters(), s -> alignToBaseDirectory(s, basedir)))
|
||||||
|
.outputDirectory(alignToBaseDirectory(build.getOutputDirectory(), basedir))
|
||||||
|
.testOutputDirectory(alignToBaseDirectory(build.getTestOutputDirectory(), basedir))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
Reporting reporting = model.getReporting();
|
||||||
|
Reporting newReporting = null;
|
||||||
|
if (reporting != null) {
|
||||||
|
newReporting = Reporting.newBuilder(reporting)
|
||||||
|
.outputDirectory(alignToBaseDirectory(reporting.getOutputDirectory(), basedir))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
if (newBuild != build || newReporting != reporting) {
|
||||||
|
model = Model.newBuilder(model)
|
||||||
|
.build(newBuild)
|
||||||
|
.reporting(newReporting)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> List<T> map(List<T> resources, Function<T, T> mapper) {
|
||||||
|
List<T> newResources = null;
|
||||||
|
if (resources != null) {
|
||||||
|
for (int i = 0; i < resources.size(); i++) {
|
||||||
|
T resource = resources.get(i);
|
||||||
|
T newResource = mapper.apply(resource);
|
||||||
|
if (newResource != null) {
|
||||||
|
if (newResources == null) {
|
||||||
|
newResources = new ArrayList<>(resources);
|
||||||
|
}
|
||||||
|
newResources.set(i, newResource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Resource alignToBaseDirectory(Resource resource, Path basedir) {
|
||||||
|
if (resource != null) {
|
||||||
|
String newDir = alignToBaseDirectory(resource.getDirectory(), basedir);
|
||||||
|
if (newDir != null) {
|
||||||
|
return resource.withDirectory(newDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String alignToBaseDirectory(String path, Path basedir) {
|
||||||
|
String newPath = pathTranslator.alignToBaseDirectory(path, basedir);
|
||||||
|
return Objects.equals(path, newPath) ? null : newPath;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelProblem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a problem that was encountered during model building. A problem can either be an exception that was thrown
|
||||||
|
* or a simple string message. In addition, a problem carries a hint about its source, e.g. the POM file that exhibits
|
||||||
|
* the problem.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class DefaultModelProblem implements ModelProblem {
|
||||||
|
|
||||||
|
private final String source;
|
||||||
|
|
||||||
|
private final int lineNumber;
|
||||||
|
|
||||||
|
private final int columnNumber;
|
||||||
|
|
||||||
|
private final String modelId;
|
||||||
|
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
private final Exception exception;
|
||||||
|
|
||||||
|
private final Severity severity;
|
||||||
|
|
||||||
|
private final Version version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new problem with the specified message and exception.
|
||||||
|
*
|
||||||
|
* @param message The message describing the problem, may be {@code null}.
|
||||||
|
* @param severity The severity level of the problem, may be {@code null} to default to
|
||||||
|
* {@link Severity#ERROR}.
|
||||||
|
* @param source The source of the problem, may be {@code null}.
|
||||||
|
* @param lineNumber The one-based index of the line containing the error or {@code -1} if unknown.
|
||||||
|
* @param columnNumber The one-based index of the column containing the error or {@code -1} if unknown.
|
||||||
|
* @param exception The exception that caused this problem, may be {@code null}.
|
||||||
|
*/
|
||||||
|
// mkleint: does this need to be public?
|
||||||
|
public DefaultModelProblem(
|
||||||
|
String message,
|
||||||
|
Severity severity,
|
||||||
|
Version version,
|
||||||
|
Model source,
|
||||||
|
int lineNumber,
|
||||||
|
int columnNumber,
|
||||||
|
Exception exception) {
|
||||||
|
this(
|
||||||
|
message,
|
||||||
|
severity,
|
||||||
|
version,
|
||||||
|
ModelProblemUtils.toPath(source),
|
||||||
|
lineNumber,
|
||||||
|
columnNumber,
|
||||||
|
ModelProblemUtils.toId(source),
|
||||||
|
exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new problem with the specified message and exception.
|
||||||
|
*
|
||||||
|
* @param message The message describing the problem, may be {@code null}.
|
||||||
|
* @param severity The severity level of the problem, may be {@code null} to default to
|
||||||
|
* {@link Severity#ERROR}.
|
||||||
|
* @param version The version since the problem is relevant
|
||||||
|
* @param source A hint about the source of the problem like a file path, may be {@code null}.
|
||||||
|
* @param lineNumber The one-based index of the line containing the problem or {@code -1} if unknown.
|
||||||
|
* @param columnNumber The one-based index of the column containing the problem or {@code -1} if unknown.
|
||||||
|
* @param modelId The identifier of the model that exhibits the problem, may be {@code null}.
|
||||||
|
* @param exception The exception that caused this problem, may be {@code null}.
|
||||||
|
*/
|
||||||
|
// mkleint: does this need to be public?
|
||||||
|
@SuppressWarnings("checkstyle:parameternumber")
|
||||||
|
public DefaultModelProblem(
|
||||||
|
String message,
|
||||||
|
Severity severity,
|
||||||
|
Version version,
|
||||||
|
String source,
|
||||||
|
int lineNumber,
|
||||||
|
int columnNumber,
|
||||||
|
String modelId,
|
||||||
|
Exception exception) {
|
||||||
|
this.message = message;
|
||||||
|
this.severity = (severity != null) ? severity : Severity.ERROR;
|
||||||
|
this.source = (source != null) ? source : "";
|
||||||
|
this.lineNumber = lineNumber;
|
||||||
|
this.columnNumber = columnNumber;
|
||||||
|
this.modelId = (modelId != null) ? modelId : "";
|
||||||
|
this.exception = exception;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLineNumber() {
|
||||||
|
return lineNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnNumber() {
|
||||||
|
return columnNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModelId() {
|
||||||
|
return modelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Exception getException() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLocation() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
String msg = null;
|
||||||
|
|
||||||
|
if (message != null && !message.isEmpty()) {
|
||||||
|
msg = message;
|
||||||
|
} else if (exception != null) {
|
||||||
|
msg = exception.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg == null) {
|
||||||
|
msg = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Severity getSeverity() {
|
||||||
|
return severity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Version getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder buffer = new StringBuilder(128);
|
||||||
|
|
||||||
|
buffer.append('[').append(getSeverity()).append("] ");
|
||||||
|
buffer.append(getMessage());
|
||||||
|
String location = ModelProblemUtils.formatLocation(this, null);
|
||||||
|
if (!location.isEmpty()) {
|
||||||
|
buffer.append(" @ ");
|
||||||
|
buffer.append(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,198 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.InputLocation;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.BuilderProblem;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderException;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderResult;
|
||||||
|
import org.apache.maven.api.services.ModelProblem;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.spi.ModelParserException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects problems that are encountered during model building. The primary purpose of this component is to account for
|
||||||
|
* the fact that the problem reporter has/should not have information about the calling context and hence cannot provide
|
||||||
|
* an expressive source hint for the model problem. Instead, the source hint is configured by the model builder before
|
||||||
|
* it delegates to other components that potentially encounter problems. Then, the problem reporter can focus on
|
||||||
|
* providing a simple error message, leaving the donkey work of creating a nice model problem to this component.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class DefaultModelProblemCollector implements ModelProblemCollector {
|
||||||
|
|
||||||
|
private final ModelBuilderResult result;
|
||||||
|
|
||||||
|
private List<ModelProblem> problems;
|
||||||
|
|
||||||
|
private String source;
|
||||||
|
|
||||||
|
private Model sourceModel;
|
||||||
|
|
||||||
|
private Model rootModel;
|
||||||
|
|
||||||
|
private Set<ModelProblem.Severity> severities = EnumSet.noneOf(ModelProblem.Severity.class);
|
||||||
|
|
||||||
|
DefaultModelProblemCollector(ModelBuilderResult result) {
|
||||||
|
this.result = result;
|
||||||
|
this.problems = result.getProblems();
|
||||||
|
|
||||||
|
for (ModelProblem problem : this.problems) {
|
||||||
|
severities.add(problem.getSeverity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasFatalErrors() {
|
||||||
|
return severities.contains(ModelProblem.Severity.FATAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasErrors() {
|
||||||
|
return severities.contains(ModelProblem.Severity.ERROR) || severities.contains(ModelProblem.Severity.FATAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ModelProblem> getProblems() {
|
||||||
|
return problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSource(String source) {
|
||||||
|
this.source = source;
|
||||||
|
this.sourceModel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSource(Model source) {
|
||||||
|
this.sourceModel = source;
|
||||||
|
this.source = null;
|
||||||
|
|
||||||
|
if (rootModel == null) {
|
||||||
|
rootModel = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSource() {
|
||||||
|
if (source == null && sourceModel != null) {
|
||||||
|
source = ModelProblemUtils.toPath(sourceModel);
|
||||||
|
}
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getModelId() {
|
||||||
|
return ModelProblemUtils.toId(sourceModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRootModel(Model rootModel) {
|
||||||
|
this.rootModel = rootModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Model getRootModel() {
|
||||||
|
return rootModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRootModelId() {
|
||||||
|
return ModelProblemUtils.toId(rootModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(ModelProblem problem) {
|
||||||
|
problems.add(problem);
|
||||||
|
|
||||||
|
severities.add(problem.getSeverity());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAll(Collection<ModelProblem> problems) {
|
||||||
|
this.problems.addAll(problems);
|
||||||
|
|
||||||
|
for (ModelProblem problem : problems) {
|
||||||
|
severities.add(problem.getSeverity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(BuilderProblem.Severity severity, ModelProblem.Version version, String message) {
|
||||||
|
add(severity, version, message, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(
|
||||||
|
BuilderProblem.Severity severity, ModelProblem.Version version, String message, InputLocation location) {
|
||||||
|
add(severity, version, message, location, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(
|
||||||
|
BuilderProblem.Severity severity, ModelProblem.Version version, String message, Exception exception) {
|
||||||
|
add(severity, version, message, null, exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(
|
||||||
|
BuilderProblem.Severity severity,
|
||||||
|
ModelProblem.Version version,
|
||||||
|
String message,
|
||||||
|
InputLocation location,
|
||||||
|
Exception exception) {
|
||||||
|
int line = -1;
|
||||||
|
int column = -1;
|
||||||
|
String source = null;
|
||||||
|
String modelId = null;
|
||||||
|
|
||||||
|
if (location != null) {
|
||||||
|
line = location.getLineNumber();
|
||||||
|
column = location.getColumnNumber();
|
||||||
|
if (location.getSource() != null) {
|
||||||
|
modelId = location.getSource().getModelId();
|
||||||
|
source = location.getSource().getLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelId == null) {
|
||||||
|
modelId = getModelId();
|
||||||
|
source = getSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line <= 0 && column <= 0 && exception instanceof ModelParserException e) {
|
||||||
|
line = e.getLineNumber();
|
||||||
|
column = e.getColumnNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelProblem problem =
|
||||||
|
new DefaultModelProblem(message, severity, version, source, line, column, modelId, exception);
|
||||||
|
|
||||||
|
add(problem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelBuilderException newModelBuilderException() {
|
||||||
|
ModelBuilderResult result = this.result;
|
||||||
|
if (result.getModelIds().isEmpty()) {
|
||||||
|
DefaultModelBuilderResult tmp = new DefaultModelBuilderResult();
|
||||||
|
tmp.setEffectiveModel(result.getEffectiveModel());
|
||||||
|
tmp.setProblems(getProblems());
|
||||||
|
tmp.setActiveExternalProfiles(result.getActiveExternalProfiles());
|
||||||
|
String id = getRootModelId();
|
||||||
|
tmp.addModelId(id);
|
||||||
|
tmp.setRawModel(id, getRootModel());
|
||||||
|
result = tmp;
|
||||||
|
}
|
||||||
|
return new ModelBuilderException(result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Inject;
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
import org.apache.maven.api.services.xml.ModelXmlFactory;
|
||||||
|
import org.apache.maven.api.services.xml.XmlReaderRequest;
|
||||||
|
import org.apache.maven.api.spi.ModelParser;
|
||||||
|
import org.apache.maven.api.spi.ModelParserException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Note: uses @Typed to limit the types it is available for injection to just ModelProcessor.
|
||||||
|
*
|
||||||
|
* This is because the ModelProcessor interface extends ModelLocator and ModelReader. If we
|
||||||
|
* made this component available under all its interfaces then it could end up being injected
|
||||||
|
* into itself leading to a stack overflow.
|
||||||
|
*
|
||||||
|
* A side effect of using @Typed is that it translates to explicit bindings in the container.
|
||||||
|
* So instead of binding the component under a 'wildcard' key it is now bound with an explicit
|
||||||
|
* key. Since this is a default component this will be a plain binding of ModelProcessor to
|
||||||
|
* this implementation type, ie. no hint/name.
|
||||||
|
*
|
||||||
|
* This leads to a second side effect in that any @Inject request for just ModelProcessor in
|
||||||
|
* the same injector is immediately matched to this explicit binding, which means extensions
|
||||||
|
* cannot override this binding. This is because the lookup is always short-circuited in this
|
||||||
|
* specific situation (plain @Inject request, and plain explicit binding for the same type.)
|
||||||
|
*
|
||||||
|
* The simplest solution is to use a custom @Named here so it isn't bound under the plain key.
|
||||||
|
* This is only necessary for default components using @Typed that want to support overriding.
|
||||||
|
*
|
||||||
|
* As a non-default component this now gets a negative priority relative to other implementations
|
||||||
|
* of the same interface. Since we want to allow overriding this doesn't matter in this case.
|
||||||
|
* (if it did we could add @Priority of 0 to match the priority given to default components.)
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultModelProcessor implements ModelProcessor {
|
||||||
|
|
||||||
|
private final ModelXmlFactory modelXmlFactory;
|
||||||
|
private final List<ModelParser> modelParsers;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DefaultModelProcessor(ModelXmlFactory modelXmlFactory, List<ModelParser> modelParsers) {
|
||||||
|
this.modelXmlFactory = modelXmlFactory;
|
||||||
|
this.modelParsers = modelParsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path locateExistingPom(Path projectDirectory) {
|
||||||
|
// Note that the ModelProcessor#locatePom never returns null
|
||||||
|
// while the ModelParser#locatePom needs to return an existing path!
|
||||||
|
Path pom = modelParsers.stream()
|
||||||
|
.map(m -> m.locate(projectDirectory)
|
||||||
|
.map(org.apache.maven.api.services.Source::getPath)
|
||||||
|
.orElse(null))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.findFirst()
|
||||||
|
.orElseGet(() -> doLocateExistingPom(projectDirectory));
|
||||||
|
if (pom != null && !pom.equals(projectDirectory) && !pom.getParent().equals(projectDirectory)) {
|
||||||
|
throw new IllegalArgumentException("The POM found does not belong to the given directory: " + pom);
|
||||||
|
}
|
||||||
|
return pom;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model read(XmlReaderRequest request) throws IOException {
|
||||||
|
Objects.requireNonNull(request, "source cannot be null");
|
||||||
|
Path pomFile = request.getPath();
|
||||||
|
if (pomFile != null) {
|
||||||
|
Path projectDirectory = pomFile.getParent();
|
||||||
|
List<ModelParserException> exceptions = new ArrayList<>();
|
||||||
|
for (ModelParser parser : modelParsers) {
|
||||||
|
try {
|
||||||
|
Optional<Model> model =
|
||||||
|
parser.locateAndParse(projectDirectory, Map.of(ModelParser.STRICT, request.isStrict()));
|
||||||
|
if (model.isPresent()) {
|
||||||
|
return model.get().withPomFile(pomFile);
|
||||||
|
}
|
||||||
|
} catch (ModelParserException e) {
|
||||||
|
exceptions.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return doRead(request);
|
||||||
|
} catch (IOException e) {
|
||||||
|
exceptions.forEach(e::addSuppressed);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return doRead(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path doLocateExistingPom(Path project) {
|
||||||
|
if (project == null) {
|
||||||
|
project = Paths.get(System.getProperty("user.dir"));
|
||||||
|
}
|
||||||
|
if (Files.isDirectory(project)) {
|
||||||
|
Path pom = project.resolve("pom.xml");
|
||||||
|
return Files.isRegularFile(pom) ? pom : null;
|
||||||
|
} else if (Files.isRegularFile(project)) {
|
||||||
|
return project;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Model doRead(XmlReaderRequest request) throws IOException {
|
||||||
|
return modelXmlFactory.read(request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelTransformerContext;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
class DefaultModelTransformerContext implements ModelTransformerContext {
|
||||||
|
final ModelProcessor modelLocator;
|
||||||
|
|
||||||
|
final Map<String, String> userProperties = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
final Map<Path, Holder> modelByPath = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
final Map<GAKey, Holder> modelByGA = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static class Holder {
|
||||||
|
private volatile boolean set;
|
||||||
|
private volatile Model model;
|
||||||
|
|
||||||
|
Holder() {}
|
||||||
|
|
||||||
|
Holder(Model model) {
|
||||||
|
this.model = Objects.requireNonNull(model);
|
||||||
|
this.set = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Model deref(Holder holder) {
|
||||||
|
return holder != null ? holder.get() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Model get() {
|
||||||
|
if (!set) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (!set) {
|
||||||
|
try {
|
||||||
|
this.wait();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Model computeIfAbsent(Supplier<Model> supplier) {
|
||||||
|
if (!set) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (!set) {
|
||||||
|
this.set = true;
|
||||||
|
this.model = supplier.get();
|
||||||
|
this.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultModelTransformerContext(ModelProcessor modelLocator) {
|
||||||
|
this.modelLocator = modelLocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUserProperty(String key) {
|
||||||
|
return userProperties.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model getRawModel(Path from, Path p) {
|
||||||
|
return Holder.deref(modelByPath.get(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model getRawModel(Path from, String groupId, String artifactId) {
|
||||||
|
return Holder.deref(modelByGA.get(new GAKey(groupId, artifactId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path locate(Path path) {
|
||||||
|
return modelLocator.locateExistingPom(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class GAKey {
|
||||||
|
private final String groupId;
|
||||||
|
private final String artifactId;
|
||||||
|
private final int hashCode;
|
||||||
|
|
||||||
|
GAKey(String groupId, String artifactId) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.artifactId = artifactId;
|
||||||
|
this.hashCode = Objects.hash(groupId, artifactId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof GAKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GAKey other = (GAKey) obj;
|
||||||
|
return Objects.equals(artifactId, other.artifactId) && Objects.equals(groupId, other.groupId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,231 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderException;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblem;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.ModelSource;
|
||||||
|
import org.apache.maven.api.services.ModelTransformerContext;
|
||||||
|
import org.apache.maven.api.services.ModelTransformerContextBuilder;
|
||||||
|
import org.apache.maven.internal.impl.model.DefaultModelTransformerContext.GAKey;
|
||||||
|
import org.apache.maven.internal.impl.model.DefaultModelTransformerContext.Holder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds up the transformer context.
|
||||||
|
* After the buildplan is ready, the build()-method returns the immutable context useful during distribution.
|
||||||
|
* This is an inner class, as it must be able to call readRawModel()
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
class DefaultModelTransformerContextBuilder implements ModelTransformerContextBuilder {
|
||||||
|
private final Graph dag = new Graph();
|
||||||
|
private final DefaultModelBuilder defaultModelBuilder;
|
||||||
|
private final DefaultModelTransformerContext context;
|
||||||
|
|
||||||
|
private final Map<String, Set<ModelSource>> mappedSources = new ConcurrentHashMap<>(64);
|
||||||
|
|
||||||
|
private volatile boolean fullReactorLoaded;
|
||||||
|
|
||||||
|
DefaultModelTransformerContextBuilder(DefaultModelBuilder defaultModelBuilder) {
|
||||||
|
this.defaultModelBuilder = defaultModelBuilder;
|
||||||
|
this.context = new DefaultModelTransformerContext(defaultModelBuilder.getModelProcessor());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If an interface could be extracted, DefaultModelProblemCollector should be ModelProblemCollectorExt
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ModelTransformerContext initialize(ModelBuilderRequest request, ModelProblemCollector collector) {
|
||||||
|
// We must assume the TransformerContext was created using this.newTransformerContextBuilder()
|
||||||
|
DefaultModelProblemCollector problems = (DefaultModelProblemCollector) collector;
|
||||||
|
return new ModelTransformerContext() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path locate(Path path) {
|
||||||
|
return context.locate(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUserProperty(String key) {
|
||||||
|
return context.userProperties.computeIfAbsent(
|
||||||
|
key, k -> request.getUserProperties().get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model getRawModel(Path from, String gId, String aId) {
|
||||||
|
Model model = findRawModel(from, gId, aId);
|
||||||
|
if (model != null) {
|
||||||
|
context.modelByGA.put(new GAKey(gId, aId), new Holder(model));
|
||||||
|
context.modelByPath.put(model.getPomFile(), new Holder(model));
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model getRawModel(Path from, Path path) {
|
||||||
|
Model model = findRawModel(from, path);
|
||||||
|
if (model != null) {
|
||||||
|
String groupId = DefaultModelBuilder.getGroupId(model);
|
||||||
|
context.modelByGA.put(
|
||||||
|
new DefaultModelTransformerContext.GAKey(groupId, model.getArtifactId()),
|
||||||
|
new Holder(model));
|
||||||
|
context.modelByPath.put(path, new Holder(model));
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Model findRawModel(Path from, String groupId, String artifactId) {
|
||||||
|
ModelSource source = getSource(groupId, artifactId);
|
||||||
|
if (source == null) {
|
||||||
|
// we need to check the whole reactor in case it's a dependency
|
||||||
|
loadFullReactor();
|
||||||
|
source = getSource(groupId, artifactId);
|
||||||
|
}
|
||||||
|
if (source != null) {
|
||||||
|
if (!addEdge(from, source.getPath(), problems)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ModelBuilderRequest gaBuildingRequest = ModelBuilderRequest.build(request, source);
|
||||||
|
return defaultModelBuilder.readRawModel(gaBuildingRequest, problems);
|
||||||
|
} catch (ModelBuilderException e) {
|
||||||
|
// gathered with problem collector
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadFullReactor() {
|
||||||
|
if (!fullReactorLoaded) {
|
||||||
|
synchronized (DefaultModelTransformerContextBuilder.this) {
|
||||||
|
if (!fullReactorLoaded) {
|
||||||
|
doLoadFullReactor();
|
||||||
|
fullReactorLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doLoadFullReactor() {
|
||||||
|
Path rootDirectory;
|
||||||
|
try {
|
||||||
|
rootDirectory = request.getSession().getRootDirectory();
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// if no root directory, bail out
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Path> toLoad = new ArrayList<>();
|
||||||
|
Path root = defaultModelBuilder.getModelProcessor().locateExistingPom(rootDirectory);
|
||||||
|
toLoad.add(root);
|
||||||
|
while (!toLoad.isEmpty()) {
|
||||||
|
Path pom = toLoad.remove(0);
|
||||||
|
try {
|
||||||
|
ModelBuilderRequest gaBuildingRequest =
|
||||||
|
ModelBuilderRequest.build(request, ModelSource.fromPath(pom));
|
||||||
|
Model rawModel = defaultModelBuilder.readFileModel(gaBuildingRequest, problems);
|
||||||
|
for (String module : rawModel.getModules()) {
|
||||||
|
Path moduleFile = defaultModelBuilder
|
||||||
|
.getModelProcessor()
|
||||||
|
.locateExistingPom(pom.getParent().resolve(module));
|
||||||
|
if (moduleFile != null) {
|
||||||
|
toLoad.add(moduleFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ModelBuilderException e) {
|
||||||
|
// gathered with problem collector
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Model findRawModel(Path from, Path p) {
|
||||||
|
if (!Files.isRegularFile(p)) {
|
||||||
|
throw new IllegalArgumentException("Not a regular file: " + p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!addEdge(from, p, problems)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelBuilderRequest req = ModelBuilderRequest.build(request, ModelSource.fromPath(p));
|
||||||
|
|
||||||
|
try {
|
||||||
|
return defaultModelBuilder.readRawModel(req, problems);
|
||||||
|
} catch (ModelBuilderException e) {
|
||||||
|
// gathered with problem collector
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean addEdge(Path from, Path p, DefaultModelProblemCollector problems) {
|
||||||
|
try {
|
||||||
|
dag.addEdge(from.toString(), p.toString());
|
||||||
|
return true;
|
||||||
|
} catch (Graph.CycleDetectedException e) {
|
||||||
|
problems.add(new DefaultModelProblem(
|
||||||
|
"Cycle detected between models at " + from + " and " + p,
|
||||||
|
ModelProblem.Severity.FATAL,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
e));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelTransformerContext build() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelSource getSource(String groupId, String artifactId) {
|
||||||
|
Set<ModelSource> sources = mappedSources.get(groupId + ":" + artifactId);
|
||||||
|
if (sources == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return sources.stream()
|
||||||
|
.reduce((a, b) -> {
|
||||||
|
throw new IllegalStateException(String.format(
|
||||||
|
"No unique Source for %s:%s: %s and %s",
|
||||||
|
groupId, artifactId, a.getLocation(), b.getLocation()));
|
||||||
|
})
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putSource(String groupId, String artifactId, ModelSource source) {
|
||||||
|
mappedSources
|
||||||
|
.computeIfAbsent(groupId + ":" + artifactId, k -> new HashSet<>())
|
||||||
|
.add(source);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maven default implementation of the {@link ModelVersionProcessor} to support
|
||||||
|
* <a href="https://maven.apache.org/maven-ci-friendly.html">CI Friendly Versions</a>
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultModelVersionProcessor implements ModelVersionProcessor {
|
||||||
|
|
||||||
|
private static final String SHA1_PROPERTY = "sha1";
|
||||||
|
|
||||||
|
private static final String CHANGELIST_PROPERTY = "changelist";
|
||||||
|
|
||||||
|
private static final String REVISION_PROPERTY = "revision";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidProperty(String property) {
|
||||||
|
return REVISION_PROPERTY.equals(property)
|
||||||
|
|| CHANGELIST_PROPERTY.equals(property)
|
||||||
|
|| SHA1_PROPERTY.equals(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void overwriteModelProperties(Properties modelProperties, ModelBuilderRequest request) {
|
||||||
|
Map<String, String> props = request.getUserProperties();
|
||||||
|
if (props.containsKey(REVISION_PROPERTY)) {
|
||||||
|
modelProperties.put(REVISION_PROPERTY, props.get(REVISION_PROPERTY));
|
||||||
|
}
|
||||||
|
if (props.containsKey(CHANGELIST_PROPERTY)) {
|
||||||
|
modelProperties.put(CHANGELIST_PROPERTY, props.get(CHANGELIST_PROPERTY));
|
||||||
|
}
|
||||||
|
if (props.containsKey(SHA1_PROPERTY)) {
|
||||||
|
modelProperties.put(SHA1_PROPERTY, props.get(SHA1_PROPERTY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves relative paths against a specific base directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultPathTranslator implements PathTranslator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String alignToBaseDirectory(String path, Path basedir) {
|
||||||
|
String result = path;
|
||||||
|
|
||||||
|
if (path != null && basedir != null) {
|
||||||
|
path = path.replace('\\', File.separatorChar).replace('/', File.separatorChar);
|
||||||
|
|
||||||
|
File file = new File(path);
|
||||||
|
if (file.isAbsolute()) {
|
||||||
|
// path was already absolute, just normalize file separator and we're done
|
||||||
|
result = file.getPath();
|
||||||
|
} else if (file.getPath().startsWith(File.separator)) {
|
||||||
|
// drive-relative Windows path, don't align with project directory but with drive root
|
||||||
|
result = file.getAbsolutePath();
|
||||||
|
} else {
|
||||||
|
// an ordinary relative path, align with project directory
|
||||||
|
result = basedir.resolve(path).normalize().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Build;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.Plugin;
|
||||||
|
import org.apache.maven.api.model.PluginContainer;
|
||||||
|
import org.apache.maven.api.model.PluginExecution;
|
||||||
|
import org.apache.maven.api.model.PluginManagement;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.PluginManagementInjector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles injection of plugin management into the model.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"checkstyle:methodname"})
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultPluginManagementInjector implements PluginManagementInjector {
|
||||||
|
|
||||||
|
private ManagementModelMerger merger = new ManagementModelMerger();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model injectManagement(Model model, ModelBuilderRequest request, ModelProblemCollector problems) {
|
||||||
|
return merger.mergeManagedBuildPlugins(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ManagementModelMerger
|
||||||
|
*/
|
||||||
|
protected static class ManagementModelMerger extends MavenModelMerger {
|
||||||
|
|
||||||
|
public Model mergeManagedBuildPlugins(Model model) {
|
||||||
|
Build build = model.getBuild();
|
||||||
|
if (build != null) {
|
||||||
|
PluginManagement pluginManagement = build.getPluginManagement();
|
||||||
|
if (pluginManagement != null) {
|
||||||
|
return model.withBuild(mergePluginContainerPlugins(build, pluginManagement));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Build mergePluginContainerPlugins(Build target, PluginContainer source) {
|
||||||
|
List<Plugin> src = source.getPlugins();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
Map<Object, Plugin> managedPlugins = new LinkedHashMap<>(src.size() * 2);
|
||||||
|
|
||||||
|
Map<Object, Object> context = Collections.emptyMap();
|
||||||
|
|
||||||
|
for (Plugin element : src) {
|
||||||
|
Object key = getPluginKey().apply(element);
|
||||||
|
managedPlugins.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Plugin> newPlugins = new ArrayList<>();
|
||||||
|
for (Plugin element : target.getPlugins()) {
|
||||||
|
Object key = getPluginKey().apply(element);
|
||||||
|
Plugin managedPlugin = managedPlugins.get(key);
|
||||||
|
if (managedPlugin != null) {
|
||||||
|
element = mergePlugin(element, managedPlugin, false, context);
|
||||||
|
}
|
||||||
|
newPlugins.add(element);
|
||||||
|
}
|
||||||
|
return target.withPlugins(newPlugins);
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergePlugin_Executions(
|
||||||
|
Plugin.Builder builder,
|
||||||
|
Plugin target,
|
||||||
|
Plugin source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<PluginExecution> src = source.getExecutions();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<PluginExecution> tgt = target.getExecutions();
|
||||||
|
|
||||||
|
Map<Object, PluginExecution> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
|
||||||
|
|
||||||
|
for (PluginExecution element : src) {
|
||||||
|
Object key = getPluginExecutionKey().apply(element);
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PluginExecution element : tgt) {
|
||||||
|
Object key = getPluginExecutionKey().apply(element);
|
||||||
|
PluginExecution existing = merged.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
element = mergePluginExecution(element, existing, sourceDominant, context);
|
||||||
|
}
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.executions(merged.values());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,190 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the environmental context used to determine the activation status of profiles.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class DefaultProfileActivationContext implements ProfileActivationContext {
|
||||||
|
|
||||||
|
private List<String> activeProfileIds = Collections.emptyList();
|
||||||
|
|
||||||
|
private List<String> inactiveProfileIds = Collections.emptyList();
|
||||||
|
|
||||||
|
private Map<String, String> systemProperties = Collections.emptyMap();
|
||||||
|
|
||||||
|
private Map<String, String> userProperties = Collections.emptyMap();
|
||||||
|
|
||||||
|
private Map<String, String> projectProperties = Collections.emptyMap();
|
||||||
|
|
||||||
|
private Path projectDirectory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getActiveProfileIds() {
|
||||||
|
return activeProfileIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the identifiers of those profiles that should be activated by explicit demand.
|
||||||
|
*
|
||||||
|
* @param activeProfileIds The identifiers of those profiles to activate, may be {@code null}.
|
||||||
|
* @return This context, never {@code null}.
|
||||||
|
*/
|
||||||
|
public DefaultProfileActivationContext setActiveProfileIds(List<String> activeProfileIds) {
|
||||||
|
this.activeProfileIds = unmodifiable(activeProfileIds);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getInactiveProfileIds() {
|
||||||
|
return inactiveProfileIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the identifiers of those profiles that should be deactivated by explicit demand.
|
||||||
|
*
|
||||||
|
* @param inactiveProfileIds The identifiers of those profiles to deactivate, may be {@code null}.
|
||||||
|
* @return This context, never {@code null}.
|
||||||
|
*/
|
||||||
|
public DefaultProfileActivationContext setInactiveProfileIds(List<String> inactiveProfileIds) {
|
||||||
|
this.inactiveProfileIds = unmodifiable(inactiveProfileIds);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getSystemProperties() {
|
||||||
|
return systemProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the system properties to use for interpolation and profile activation. The system properties are collected
|
||||||
|
* from the runtime environment like {@link System#getProperties()} and environment variables.
|
||||||
|
*
|
||||||
|
* @param systemProperties The system properties, may be {@code null}.
|
||||||
|
* @return This context, never {@code null}.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public DefaultProfileActivationContext setSystemProperties(Properties systemProperties) {
|
||||||
|
return setSystemProperties(toMap(systemProperties));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the system properties to use for interpolation and profile activation. The system properties are collected
|
||||||
|
* from the runtime environment like {@link System#getProperties()} and environment variables.
|
||||||
|
*
|
||||||
|
* @param systemProperties The system properties, may be {@code null}.
|
||||||
|
* @return This context, never {@code null}.
|
||||||
|
*/
|
||||||
|
public DefaultProfileActivationContext setSystemProperties(Map<String, String> systemProperties) {
|
||||||
|
this.systemProperties = unmodifiable(systemProperties);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getUserProperties() {
|
||||||
|
return userProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user properties to use for interpolation and profile activation. The user properties have been
|
||||||
|
* configured directly by the user on his discretion, e.g. via the {@code -Dkey=value} parameter on the command
|
||||||
|
* line.
|
||||||
|
*
|
||||||
|
* @param userProperties The user properties, may be {@code null}.
|
||||||
|
* @return This context, never {@code null}.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public DefaultProfileActivationContext setUserProperties(Properties userProperties) {
|
||||||
|
return setUserProperties(toMap(userProperties));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user properties to use for interpolation and profile activation. The user properties have been
|
||||||
|
* configured directly by the user on his discretion, e.g. via the {@code -Dkey=value} parameter on the command
|
||||||
|
* line.
|
||||||
|
*
|
||||||
|
* @param userProperties The user properties, may be {@code null}.
|
||||||
|
* @return This context, never {@code null}.
|
||||||
|
*/
|
||||||
|
public DefaultProfileActivationContext setUserProperties(Map<String, String> userProperties) {
|
||||||
|
this.userProperties = unmodifiable(userProperties);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path getProjectDirectory() {
|
||||||
|
return projectDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the base directory of the current project.
|
||||||
|
*
|
||||||
|
* @param projectDirectory The base directory of the current project, may be {@code null} if profile activation
|
||||||
|
* happens in the context of metadata retrieval rather than project building.
|
||||||
|
* @return This context, never {@code null}.
|
||||||
|
*/
|
||||||
|
public DefaultProfileActivationContext setProjectDirectory(Path projectDirectory) {
|
||||||
|
this.projectDirectory = projectDirectory;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getProjectProperties() {
|
||||||
|
return projectProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultProfileActivationContext setProjectProperties(Properties projectProperties) {
|
||||||
|
return setProjectProperties(toMap(projectProperties));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultProfileActivationContext setProjectProperties(Map<String, String> projectProperties) {
|
||||||
|
this.projectProperties = unmodifiable(projectProperties);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> unmodifiable(List<String> list) {
|
||||||
|
return list != null ? Collections.unmodifiableList(list) : Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> unmodifiable(Map<String, String> map) {
|
||||||
|
return map != null ? Collections.unmodifiableMap(map) : Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> toMap(Properties properties) {
|
||||||
|
if (properties != null && !properties.isEmpty()) {
|
||||||
|
return properties.entrySet().stream()
|
||||||
|
.collect(Collectors.toMap(e -> String.valueOf(e.getKey()), e -> String.valueOf(e.getValue())));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,242 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Build;
|
||||||
|
import org.apache.maven.api.model.BuildBase;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.ModelBase;
|
||||||
|
import org.apache.maven.api.model.Plugin;
|
||||||
|
import org.apache.maven.api.model.PluginContainer;
|
||||||
|
import org.apache.maven.api.model.PluginExecution;
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
import org.apache.maven.api.model.ReportPlugin;
|
||||||
|
import org.apache.maven.api.model.ReportSet;
|
||||||
|
import org.apache.maven.api.model.Reporting;
|
||||||
|
import org.apache.maven.api.services.ModelBuilderRequest;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles profile injection into the model.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultProfileInjector implements ProfileInjector {
|
||||||
|
|
||||||
|
private static final Map<Model, Map<List<Profile>, Model>> CACHE = Collections.synchronizedMap(new WeakHashMap<>());
|
||||||
|
|
||||||
|
// In order for the weak hash map to work correctly, we must not hold any reference to
|
||||||
|
// the model used as the key. So we use a dummy model as a placeholder to indicate that
|
||||||
|
// we want to store the model used as they key.
|
||||||
|
private static final Model KEY = Model.newInstance();
|
||||||
|
|
||||||
|
private final ProfileModelMerger merger = new ProfileModelMerger();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model injectProfiles(
|
||||||
|
Model model, List<Profile> profiles, ModelBuilderRequest request, ModelProblemCollector problems) {
|
||||||
|
Model result = CACHE.computeIfAbsent(model, k -> new ConcurrentHashMap<>())
|
||||||
|
.computeIfAbsent(profiles, l -> doInjectProfiles(model, profiles));
|
||||||
|
return result == KEY ? model : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Model doInjectProfiles(Model model, List<Profile> profiles) {
|
||||||
|
Model orgModel = model;
|
||||||
|
for (Profile profile : profiles) {
|
||||||
|
if (profile != null) {
|
||||||
|
Model.Builder builder = Model.newBuilder(model);
|
||||||
|
merger.mergeModelBase(builder, model, profile);
|
||||||
|
|
||||||
|
if (profile.getBuild() != null) {
|
||||||
|
Build build = model.getBuild() != null ? model.getBuild() : Build.newInstance();
|
||||||
|
Build.Builder bbuilder = Build.newBuilder(build);
|
||||||
|
merger.mergeBuildBase(bbuilder, build, profile.getBuild());
|
||||||
|
builder.build(bbuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
model = builder.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model == orgModel ? KEY : model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProfileModelMerger
|
||||||
|
*/
|
||||||
|
protected static class ProfileModelMerger extends MavenModelMerger {
|
||||||
|
|
||||||
|
public void mergeModelBase(ModelBase.Builder builder, ModelBase target, ModelBase source) {
|
||||||
|
mergeModelBase(builder, target, source, true, Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mergeBuildBase(BuildBase.Builder builder, BuildBase target, BuildBase source) {
|
||||||
|
mergeBuildBase(builder, target, source, true, Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergePluginContainer_Plugins(
|
||||||
|
PluginContainer.Builder builder,
|
||||||
|
PluginContainer target,
|
||||||
|
PluginContainer source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<Plugin> src = source.getPlugins();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<Plugin> tgt = target.getPlugins();
|
||||||
|
Map<Object, Plugin> master = new LinkedHashMap<>(tgt.size() * 2);
|
||||||
|
|
||||||
|
for (Plugin element : tgt) {
|
||||||
|
Object key = getPluginKey().apply(element);
|
||||||
|
master.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Object, List<Plugin>> predecessors = new LinkedHashMap<>();
|
||||||
|
List<Plugin> pending = new ArrayList<>();
|
||||||
|
for (Plugin element : src) {
|
||||||
|
Object key = getPluginKey().apply(element);
|
||||||
|
Plugin existing = master.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
existing = mergePlugin(existing, element, sourceDominant, context);
|
||||||
|
master.put(key, existing);
|
||||||
|
if (!pending.isEmpty()) {
|
||||||
|
predecessors.put(key, pending);
|
||||||
|
pending = new ArrayList<>();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pending.add(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Plugin> result = new ArrayList<>(src.size() + tgt.size());
|
||||||
|
for (Map.Entry<Object, Plugin> entry : master.entrySet()) {
|
||||||
|
List<Plugin> pre = predecessors.get(entry.getKey());
|
||||||
|
if (pre != null) {
|
||||||
|
result.addAll(pre);
|
||||||
|
}
|
||||||
|
result.add(entry.getValue());
|
||||||
|
}
|
||||||
|
result.addAll(pending);
|
||||||
|
|
||||||
|
builder.plugins(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergePlugin_Executions(
|
||||||
|
Plugin.Builder builder,
|
||||||
|
Plugin target,
|
||||||
|
Plugin source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<PluginExecution> src = source.getExecutions();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<PluginExecution> tgt = target.getExecutions();
|
||||||
|
Map<Object, PluginExecution> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
|
||||||
|
|
||||||
|
for (PluginExecution element : tgt) {
|
||||||
|
Object key = getPluginExecutionKey().apply(element);
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PluginExecution element : src) {
|
||||||
|
Object key = getPluginExecutionKey().apply(element);
|
||||||
|
PluginExecution existing = merged.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
element = mergePluginExecution(existing, element, sourceDominant, context);
|
||||||
|
}
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.executions(merged.values());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeReporting_Plugins(
|
||||||
|
Reporting.Builder builder,
|
||||||
|
Reporting target,
|
||||||
|
Reporting source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<ReportPlugin> src = source.getPlugins();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<ReportPlugin> tgt = target.getPlugins();
|
||||||
|
Map<Object, ReportPlugin> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
|
||||||
|
|
||||||
|
for (ReportPlugin element : tgt) {
|
||||||
|
Object key = getReportPluginKey().apply(element);
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ReportPlugin element : src) {
|
||||||
|
Object key = getReportPluginKey().apply(element);
|
||||||
|
ReportPlugin existing = merged.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
element = mergeReportPlugin(existing, element, sourceDominant, context);
|
||||||
|
}
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.plugins(merged.values());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeReportPlugin_ReportSets(
|
||||||
|
ReportPlugin.Builder builder,
|
||||||
|
ReportPlugin target,
|
||||||
|
ReportPlugin source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<ReportSet> src = source.getReportSets();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<ReportSet> tgt = target.getReportSets();
|
||||||
|
Map<Object, ReportSet> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
|
||||||
|
|
||||||
|
for (ReportSet element : tgt) {
|
||||||
|
Object key = getReportSetKey().apply(element);
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ReportSet element : src) {
|
||||||
|
Object key = getReportSetKey().apply(element);
|
||||||
|
ReportSet existing = merged.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
element = mergeReportSet(existing, element, sourceDominant, context);
|
||||||
|
}
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.reportSets(merged.values());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Inject;
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Activation;
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
import org.apache.maven.api.services.BuilderProblem.Severity;
|
||||||
|
import org.apache.maven.api.services.ModelProblem.Version;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the active profiles among a given collection of profiles.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class DefaultProfileSelector implements ProfileSelector {
|
||||||
|
|
||||||
|
private final List<ProfileActivator> activators;
|
||||||
|
|
||||||
|
public DefaultProfileSelector() {
|
||||||
|
this.activators = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DefaultProfileSelector(List<ProfileActivator> activators) {
|
||||||
|
this.activators = new ArrayList<>(activators);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultProfileSelector addProfileActivator(ProfileActivator profileActivator) {
|
||||||
|
if (profileActivator != null) {
|
||||||
|
activators.add(profileActivator);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Profile> getActiveProfiles(
|
||||||
|
Collection<Profile> profiles, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||||
|
Collection<String> activatedIds = new HashSet<>(context.getActiveProfileIds());
|
||||||
|
Collection<String> deactivatedIds = new HashSet<>(context.getInactiveProfileIds());
|
||||||
|
|
||||||
|
List<Profile> activeProfiles = new ArrayList<>(profiles.size());
|
||||||
|
List<Profile> activePomProfilesByDefault = new ArrayList<>();
|
||||||
|
boolean activatedPomProfileNotByDefault = false;
|
||||||
|
|
||||||
|
for (Profile profile : profiles) {
|
||||||
|
if (!deactivatedIds.contains(profile.getId())) {
|
||||||
|
if (activatedIds.contains(profile.getId()) || isActive(profile, context, problems)) {
|
||||||
|
activeProfiles.add(profile);
|
||||||
|
if (Profile.SOURCE_POM.equals(profile.getSource())) {
|
||||||
|
activatedPomProfileNotByDefault = true;
|
||||||
|
}
|
||||||
|
} else if (isActiveByDefault(profile)) {
|
||||||
|
if (Profile.SOURCE_POM.equals(profile.getSource())) {
|
||||||
|
activePomProfilesByDefault.add(profile);
|
||||||
|
} else {
|
||||||
|
activeProfiles.add(profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!activatedPomProfileNotByDefault) {
|
||||||
|
activeProfiles.addAll(activePomProfilesByDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
return activeProfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||||
|
boolean isActive = false;
|
||||||
|
for (ProfileActivator activator : activators) {
|
||||||
|
if (activator.presentInConfig(profile, context, problems)) {
|
||||||
|
isActive = true;
|
||||||
|
try {
|
||||||
|
if (!activator.isActive(profile, context, problems)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
problems.add(
|
||||||
|
Severity.ERROR,
|
||||||
|
Version.BASE,
|
||||||
|
"Failed to determine activation for profile " + profile.getId(),
|
||||||
|
profile.getLocation(""),
|
||||||
|
e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isActiveByDefault(Profile profile) {
|
||||||
|
Activation activation = profile.getActivation();
|
||||||
|
return activation != null && activation.isActiveByDefault();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import javax.xml.stream.XMLStreamException;
|
||||||
|
import javax.xml.stream.XMLStreamReader;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import com.ctc.wstx.stax.WstxInputFactory;
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.services.model.*;
|
||||||
|
|
||||||
|
@Named
|
||||||
|
public class DefaultRootLocator implements RootLocator {
|
||||||
|
|
||||||
|
public boolean isRootDirectory(Path dir) {
|
||||||
|
if (Files.isDirectory(dir.resolve(".mvn"))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// we're too early to use the modelProcessor ...
|
||||||
|
Path pom = dir.resolve("pom.xml");
|
||||||
|
try (InputStream is = Files.newInputStream(pom)) {
|
||||||
|
XMLStreamReader parser = new WstxInputFactory().createXMLStreamReader(is);
|
||||||
|
if (parser.nextTag() == XMLStreamReader.START_ELEMENT
|
||||||
|
&& parser.getLocalName().equals("project")) {
|
||||||
|
for (int i = 0; i < parser.getAttributeCount(); i++) {
|
||||||
|
if ("root".equals(parser.getAttributeLocalName(i))) {
|
||||||
|
return Boolean.parseBoolean(parser.getAttributeValue(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException | XMLStreamException e) {
|
||||||
|
// The root locator can be used very early during the setup of Maven,
|
||||||
|
// even before the arguments from the command line are parsed. Any exception
|
||||||
|
// that would happen here should cause the build to fail at a later stage
|
||||||
|
// (when actually parsing the POM) and will lead to a better exception being
|
||||||
|
// displayed to the user, so just bail out and return false.
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,216 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Build;
|
||||||
|
import org.apache.maven.api.model.BuildBase;
|
||||||
|
import org.apache.maven.api.model.CiManagement;
|
||||||
|
import org.apache.maven.api.model.Dependency;
|
||||||
|
import org.apache.maven.api.model.DependencyManagement;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.ModelBase;
|
||||||
|
import org.apache.maven.api.model.Plugin;
|
||||||
|
import org.apache.maven.api.model.PluginContainer;
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
import org.apache.maven.api.model.ReportPlugin;
|
||||||
|
import org.apache.maven.api.model.Reporting;
|
||||||
|
import org.apache.maven.model.v4.MavenMerger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As long as Maven controls the BuildPomXMLFilter, the entities that need merging are known.
|
||||||
|
* All others can simply be copied from source to target to restore the locationTracker
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
class FileToRawModelMerger extends MavenMerger {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeBuild_Extensions(
|
||||||
|
Build.Builder builder, Build target, Build source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeBuildBase_Resources(
|
||||||
|
BuildBase.Builder builder,
|
||||||
|
BuildBase target,
|
||||||
|
BuildBase source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeBuildBase_TestResources(
|
||||||
|
BuildBase.Builder builder,
|
||||||
|
BuildBase target,
|
||||||
|
BuildBase source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeCiManagement_Notifiers(
|
||||||
|
CiManagement.Builder builder,
|
||||||
|
CiManagement target,
|
||||||
|
CiManagement source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeDependencyManagement_Dependencies(
|
||||||
|
DependencyManagement.Builder builder,
|
||||||
|
DependencyManagement target,
|
||||||
|
DependencyManagement source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
Iterator<Dependency> sourceIterator = source.getDependencies().iterator();
|
||||||
|
builder.dependencies(target.getDependencies().stream()
|
||||||
|
.map(d -> mergeDependency(d, sourceIterator.next(), sourceDominant, context))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeDependency_Exclusions(
|
||||||
|
Dependency.Builder builder,
|
||||||
|
Dependency target,
|
||||||
|
Dependency source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_Contributors(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_Developers(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_Licenses(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_MailingLists(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_Profiles(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
Iterator<Profile> sourceIterator = source.getProfiles().iterator();
|
||||||
|
builder.profiles(target.getProfiles().stream()
|
||||||
|
.map(d -> mergeProfile(d, sourceIterator.next(), sourceDominant, context))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModelBase_Dependencies(
|
||||||
|
ModelBase.Builder builder,
|
||||||
|
ModelBase target,
|
||||||
|
ModelBase source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
Iterator<Dependency> sourceIterator = source.getDependencies().iterator();
|
||||||
|
builder.dependencies(target.getDependencies().stream()
|
||||||
|
.map(d -> mergeDependency(d, sourceIterator.next(), sourceDominant, context))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModelBase_PluginRepositories(
|
||||||
|
ModelBase.Builder builder,
|
||||||
|
ModelBase target,
|
||||||
|
ModelBase source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
builder.pluginRepositories(source.getPluginRepositories());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModelBase_Repositories(
|
||||||
|
ModelBase.Builder builder,
|
||||||
|
ModelBase target,
|
||||||
|
ModelBase source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergePlugin_Dependencies(
|
||||||
|
Plugin.Builder builder, Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
Iterator<Dependency> sourceIterator = source.getDependencies().iterator();
|
||||||
|
builder.dependencies(target.getDependencies().stream()
|
||||||
|
.map(d -> mergeDependency(d, sourceIterator.next(), sourceDominant, context))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergePlugin_Executions(
|
||||||
|
Plugin.Builder builder, Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeReporting_Plugins(
|
||||||
|
Reporting.Builder builder,
|
||||||
|
Reporting target,
|
||||||
|
Reporting source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeReportPlugin_ReportSets(
|
||||||
|
ReportPlugin.Builder builder,
|
||||||
|
ReportPlugin target,
|
||||||
|
ReportPlugin source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergePluginContainer_Plugins(
|
||||||
|
PluginContainer.Builder builder,
|
||||||
|
PluginContainer target,
|
||||||
|
PluginContainer source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
// don't merge
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
class Graph {
|
||||||
|
|
||||||
|
final Map<String, Set<String>> graph = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
synchronized void addEdge(String from, String to) throws CycleDetectedException {
|
||||||
|
if (graph.computeIfAbsent(from, l -> new HashSet<>()).add(to)) {
|
||||||
|
List<String> cycle = visitCycle(graph, Collections.singleton(to), new HashMap<>(), new LinkedList<>());
|
||||||
|
if (cycle != null) {
|
||||||
|
// remove edge which introduced cycle
|
||||||
|
throw new CycleDetectedException(
|
||||||
|
"Edge between '" + from + "' and '" + to + "' introduces to cycle in the graph", cycle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum DfsState {
|
||||||
|
VISITING,
|
||||||
|
VISITED
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> visitCycle(
|
||||||
|
Map<String, Set<String>> graph,
|
||||||
|
Collection<String> children,
|
||||||
|
Map<String, DfsState> stateMap,
|
||||||
|
LinkedList<String> cycle) {
|
||||||
|
if (children != null) {
|
||||||
|
for (String v : children) {
|
||||||
|
DfsState state = stateMap.putIfAbsent(v, DfsState.VISITING);
|
||||||
|
if (state == null) {
|
||||||
|
cycle.addLast(v);
|
||||||
|
List<String> ret = visitCycle(graph, graph.get(v), stateMap, cycle);
|
||||||
|
if (ret != null) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
cycle.removeLast();
|
||||||
|
stateMap.put(v, DfsState.VISITED);
|
||||||
|
} else if (state == DfsState.VISITING) {
|
||||||
|
// we are already visiting this vertex, this mean we have a cycle
|
||||||
|
int pos = cycle.lastIndexOf(v);
|
||||||
|
List<String> ret = cycle.subList(pos, cycle.size());
|
||||||
|
ret.add(v);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CycleDetectedException extends Exception {
|
||||||
|
private final List<String> cycle;
|
||||||
|
|
||||||
|
CycleDetectedException(String message, List<String> cycle) {
|
||||||
|
super(message);
|
||||||
|
this.cycle = cycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getCycle() {
|
||||||
|
return cycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return super.getMessage() + " " + String.join(" --> ", cycle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MavenBuildTimestamp
|
||||||
|
*/
|
||||||
|
public class MavenBuildTimestamp {
|
||||||
|
// ISO 8601-compliant timestamp for machine readability
|
||||||
|
public static final String DEFAULT_BUILD_TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||||
|
|
||||||
|
public static final String BUILD_TIMESTAMP_FORMAT_PROPERTY = "maven.build.timestamp.format";
|
||||||
|
|
||||||
|
public static final TimeZone DEFAULT_BUILD_TIME_ZONE = TimeZone.getTimeZone("Etc/UTC");
|
||||||
|
|
||||||
|
private String formattedTimestamp;
|
||||||
|
|
||||||
|
public MavenBuildTimestamp() {
|
||||||
|
this(Instant.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
public MavenBuildTimestamp(Instant time) {
|
||||||
|
this(time, DEFAULT_BUILD_TIMESTAMP_FORMAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MavenBuildTimestamp(Instant time, Map<String, String> properties) {
|
||||||
|
this(time, properties != null ? properties.get(BUILD_TIMESTAMP_FORMAT_PROPERTY) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #MavenBuildTimestamp(Instant, Map)} or extract the format and pass it
|
||||||
|
* to {@link #MavenBuildTimestamp(Instant, String)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public MavenBuildTimestamp(Instant time, Properties properties) {
|
||||||
|
this(time, properties != null ? properties.getProperty(BUILD_TIMESTAMP_FORMAT_PROPERTY) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MavenBuildTimestamp(Instant time, String timestampFormat) {
|
||||||
|
if (timestampFormat == null) {
|
||||||
|
timestampFormat = DEFAULT_BUILD_TIMESTAMP_FORMAT;
|
||||||
|
}
|
||||||
|
if (time == null) {
|
||||||
|
time = Instant.now();
|
||||||
|
}
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat(timestampFormat);
|
||||||
|
dateFormat.setCalendar(new GregorianCalendar());
|
||||||
|
dateFormat.setTimeZone(DEFAULT_BUILD_TIME_ZONE);
|
||||||
|
formattedTimestamp = dateFormat.format(new Date(time.toEpochMilli()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String formattedTimestamp() {
|
||||||
|
return formattedTimestamp;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,621 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.BuildBase;
|
||||||
|
import org.apache.maven.api.model.CiManagement;
|
||||||
|
import org.apache.maven.api.model.Dependency;
|
||||||
|
import org.apache.maven.api.model.DeploymentRepository;
|
||||||
|
import org.apache.maven.api.model.DistributionManagement;
|
||||||
|
import org.apache.maven.api.model.Exclusion;
|
||||||
|
import org.apache.maven.api.model.Extension;
|
||||||
|
import org.apache.maven.api.model.InputLocation;
|
||||||
|
import org.apache.maven.api.model.IssueManagement;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.ModelBase;
|
||||||
|
import org.apache.maven.api.model.Organization;
|
||||||
|
import org.apache.maven.api.model.Plugin;
|
||||||
|
import org.apache.maven.api.model.PluginExecution;
|
||||||
|
import org.apache.maven.api.model.ReportPlugin;
|
||||||
|
import org.apache.maven.api.model.ReportSet;
|
||||||
|
import org.apache.maven.api.model.Repository;
|
||||||
|
import org.apache.maven.api.model.RepositoryBase;
|
||||||
|
import org.apache.maven.api.model.Scm;
|
||||||
|
import org.apache.maven.api.model.Site;
|
||||||
|
import org.apache.maven.model.v4.MavenMerger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The domain-specific model merger for the Maven POM, overriding generic code from parent class when necessary with
|
||||||
|
* more adapted algorithms.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MavenModelMerger extends MavenMerger {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hint key for the child path adjustment used during inheritance for URL calculations.
|
||||||
|
*/
|
||||||
|
public static final String CHILD_PATH_ADJUSTMENT = "child-path-adjustment";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The context key for the artifact id of the target model.
|
||||||
|
*/
|
||||||
|
public static final String ARTIFACT_ID = "artifact-id";
|
||||||
|
|
||||||
|
public MavenModelMerger() {
|
||||||
|
super(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model merge(Model target, Model source, boolean sourceDominant, Map<?, ?> hints) {
|
||||||
|
return super.merge(target, source, sourceDominant, hints);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Model mergeModel(Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
context.put(ARTIFACT_ID, target.getArtifactId());
|
||||||
|
|
||||||
|
return super.mergeModel(target, source, sourceDominant, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_Name(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
String src = source.getName();
|
||||||
|
if (src != null) {
|
||||||
|
if (sourceDominant) {
|
||||||
|
builder.name(src);
|
||||||
|
builder.location("name", source.getLocation("name"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_Url(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
String src = source.getUrl();
|
||||||
|
if (src != null) {
|
||||||
|
if (sourceDominant) {
|
||||||
|
builder.url(src);
|
||||||
|
builder.location("url", source.getLocation("url"));
|
||||||
|
} else if (target.getUrl() == null) {
|
||||||
|
builder.url(extrapolateChildUrl(src, source.isChildProjectUrlInheritAppendPath(), context));
|
||||||
|
builder.location("url", source.getLocation("url"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: Whether the merge continues recursively into an existing node or not could be an option for the generated
|
||||||
|
* merger
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_Organization(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
Organization src = source.getOrganization();
|
||||||
|
if (src != null) {
|
||||||
|
Organization tgt = target.getOrganization();
|
||||||
|
if (tgt == null) {
|
||||||
|
builder.organization(src);
|
||||||
|
builder.location("organisation", source.getLocation("organisation"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_IssueManagement(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
IssueManagement src = source.getIssueManagement();
|
||||||
|
if (src != null) {
|
||||||
|
IssueManagement tgt = target.getIssueManagement();
|
||||||
|
if (tgt == null) {
|
||||||
|
builder.issueManagement(src);
|
||||||
|
builder.location("issueManagement", source.getLocation("issueManagement"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_CiManagement(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
CiManagement src = source.getCiManagement();
|
||||||
|
if (src != null) {
|
||||||
|
CiManagement tgt = target.getCiManagement();
|
||||||
|
if (tgt == null) {
|
||||||
|
builder.ciManagement(src);
|
||||||
|
builder.location("ciManagement", source.getLocation("ciManagement"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_ModelVersion(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
// neither inherited nor injected
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_ArtifactId(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
// neither inherited nor injected
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_Profiles(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
// neither inherited nor injected
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_Prerequisites(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
// neither inherited nor injected
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_Licenses(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
builder.licenses(target.getLicenses().isEmpty() ? source.getLicenses() : target.getLicenses());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_Developers(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
builder.developers(target.getDevelopers().isEmpty() ? source.getDevelopers() : target.getDevelopers());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_Contributors(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
builder.contributors(target.getContributors().isEmpty() ? source.getContributors() : target.getContributors());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModel_MailingLists(
|
||||||
|
Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
if (target.getMailingLists().isEmpty()) {
|
||||||
|
builder.mailingLists(source.getMailingLists());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModelBase_Modules(
|
||||||
|
ModelBase.Builder builder,
|
||||||
|
ModelBase target,
|
||||||
|
ModelBase source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<String> src = source.getModules();
|
||||||
|
if (!src.isEmpty() && sourceDominant) {
|
||||||
|
List<Integer> indices = new ArrayList<>();
|
||||||
|
List<String> tgt = target.getModules();
|
||||||
|
Set<String> excludes = new LinkedHashSet<>(tgt);
|
||||||
|
List<String> merged = new ArrayList<>(tgt.size() + src.size());
|
||||||
|
merged.addAll(tgt);
|
||||||
|
for (int i = 0, n = tgt.size(); i < n; i++) {
|
||||||
|
indices.add(i);
|
||||||
|
}
|
||||||
|
for (int i = 0, n = src.size(); i < n; i++) {
|
||||||
|
String s = src.get(i);
|
||||||
|
if (!excludes.contains(s)) {
|
||||||
|
merged.add(s);
|
||||||
|
indices.add(~i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.modules(merged);
|
||||||
|
builder.location(
|
||||||
|
"modules",
|
||||||
|
InputLocation.merge(target.getLocation("modules"), source.getLocation("modules"), indices));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: The order of the merged list could be controlled by an attribute in the model association: target-first,
|
||||||
|
* source-first, dominant-first, recessive-first
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void mergeModelBase_Repositories(
|
||||||
|
ModelBase.Builder builder,
|
||||||
|
ModelBase target,
|
||||||
|
ModelBase source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<Repository> src = source.getRepositories();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<Repository> tgt = target.getRepositories();
|
||||||
|
Map<Object, Repository> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
|
||||||
|
|
||||||
|
List<Repository> dominant, recessive;
|
||||||
|
if (sourceDominant) {
|
||||||
|
dominant = src;
|
||||||
|
recessive = tgt;
|
||||||
|
} else {
|
||||||
|
dominant = tgt;
|
||||||
|
recessive = src;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Repository element : dominant) {
|
||||||
|
Object key = getRepositoryKey().apply(element);
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Repository element : recessive) {
|
||||||
|
Object key = getRepositoryKey().apply(element);
|
||||||
|
if (!merged.containsKey(key)) {
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.repositories(merged.values());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeModelBase_PluginRepositories(
|
||||||
|
ModelBase.Builder builder,
|
||||||
|
ModelBase target,
|
||||||
|
ModelBase source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<Repository> src = source.getPluginRepositories();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<Repository> tgt = target.getPluginRepositories();
|
||||||
|
Map<Object, Repository> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
|
||||||
|
|
||||||
|
List<Repository> dominant, recessive;
|
||||||
|
if (sourceDominant) {
|
||||||
|
dominant = src;
|
||||||
|
recessive = tgt;
|
||||||
|
} else {
|
||||||
|
dominant = tgt;
|
||||||
|
recessive = src;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Repository element : dominant) {
|
||||||
|
Object key = getRepositoryKey().apply(element);
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Repository element : recessive) {
|
||||||
|
Object key = getRepositoryKey().apply(element);
|
||||||
|
if (!merged.containsKey(key)) {
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.pluginRepositories(merged.values());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: Whether duplicates should be removed looks like an option for the generated merger.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void mergeBuildBase_Filters(
|
||||||
|
BuildBase.Builder builder,
|
||||||
|
BuildBase target,
|
||||||
|
BuildBase source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<String> src = source.getFilters();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<String> tgt = target.getFilters();
|
||||||
|
Set<String> excludes = new LinkedHashSet<>(tgt);
|
||||||
|
List<String> merged = new ArrayList<>(tgt.size() + src.size());
|
||||||
|
merged.addAll(tgt);
|
||||||
|
for (String s : src) {
|
||||||
|
if (!excludes.contains(s)) {
|
||||||
|
merged.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.filters(merged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeBuildBase_Resources(
|
||||||
|
BuildBase.Builder builder,
|
||||||
|
BuildBase target,
|
||||||
|
BuildBase source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
if (sourceDominant || target.getResources().isEmpty()) {
|
||||||
|
super.mergeBuildBase_Resources(builder, target, source, sourceDominant, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeBuildBase_TestResources(
|
||||||
|
BuildBase.Builder builder,
|
||||||
|
BuildBase target,
|
||||||
|
BuildBase source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
if (sourceDominant || target.getTestResources().isEmpty()) {
|
||||||
|
super.mergeBuildBase_TestResources(builder, target, source, sourceDominant, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeDistributionManagement_Relocation(
|
||||||
|
DistributionManagement.Builder builder,
|
||||||
|
DistributionManagement target,
|
||||||
|
DistributionManagement source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeDistributionManagement_Repository(
|
||||||
|
DistributionManagement.Builder builder,
|
||||||
|
DistributionManagement target,
|
||||||
|
DistributionManagement source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
DeploymentRepository src = source.getRepository();
|
||||||
|
if (src != null) {
|
||||||
|
DeploymentRepository tgt = target.getRepository();
|
||||||
|
if (sourceDominant || tgt == null) {
|
||||||
|
tgt = DeploymentRepository.newInstance(false);
|
||||||
|
builder.repository(mergeDeploymentRepository(tgt, src, sourceDominant, context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeDistributionManagement_SnapshotRepository(
|
||||||
|
DistributionManagement.Builder builder,
|
||||||
|
DistributionManagement target,
|
||||||
|
DistributionManagement source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
DeploymentRepository src = source.getSnapshotRepository();
|
||||||
|
if (src != null) {
|
||||||
|
DeploymentRepository tgt = target.getSnapshotRepository();
|
||||||
|
if (sourceDominant || tgt == null) {
|
||||||
|
tgt = DeploymentRepository.newInstance(false);
|
||||||
|
builder.snapshotRepository(mergeDeploymentRepository(tgt, src, sourceDominant, context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeDistributionManagement_Site(
|
||||||
|
DistributionManagement.Builder builder,
|
||||||
|
DistributionManagement target,
|
||||||
|
DistributionManagement source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
Site src = source.getSite();
|
||||||
|
if (src != null) {
|
||||||
|
Site tgt = target.getSite();
|
||||||
|
if (tgt == null) {
|
||||||
|
tgt = Site.newBuilder(false).build();
|
||||||
|
}
|
||||||
|
Site.Builder sbuilder = Site.newBuilder(tgt);
|
||||||
|
if (sourceDominant || tgt == null || isSiteEmpty(tgt)) {
|
||||||
|
mergeSite(sbuilder, tgt, src, sourceDominant, context);
|
||||||
|
}
|
||||||
|
super.mergeSite_ChildSiteUrlInheritAppendPath(sbuilder, tgt, src, sourceDominant, context);
|
||||||
|
builder.site(sbuilder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeSite_ChildSiteUrlInheritAppendPath(
|
||||||
|
Site.Builder builder, Site target, Site source, boolean sourceDominant, Map<Object, Object> context) {}
|
||||||
|
|
||||||
|
protected boolean isSiteEmpty(Site site) {
|
||||||
|
return (site.getId() == null || site.getId().isEmpty())
|
||||||
|
&& (site.getName() == null || site.getName().isEmpty())
|
||||||
|
&& (site.getUrl() == null || site.getUrl().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeSite_Url(
|
||||||
|
Site.Builder builder, Site target, Site source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
String src = source.getUrl();
|
||||||
|
if (src != null) {
|
||||||
|
if (sourceDominant) {
|
||||||
|
builder.url(src);
|
||||||
|
builder.location("url", source.getLocation("url"));
|
||||||
|
} else if (target.getUrl() == null) {
|
||||||
|
builder.url(extrapolateChildUrl(src, source.isChildSiteUrlInheritAppendPath(), context));
|
||||||
|
builder.location("url", source.getLocation("url"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeScm_Url(
|
||||||
|
Scm.Builder builder, Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
String src = source.getUrl();
|
||||||
|
if (src != null) {
|
||||||
|
if (sourceDominant) {
|
||||||
|
builder.url(src);
|
||||||
|
builder.location("url", source.getLocation("url"));
|
||||||
|
} else if (target.getUrl() == null) {
|
||||||
|
builder.url(extrapolateChildUrl(src, source.isChildScmUrlInheritAppendPath(), context));
|
||||||
|
builder.location("url", source.getLocation("url"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeScm_Connection(
|
||||||
|
Scm.Builder builder, Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
String src = source.getConnection();
|
||||||
|
if (src != null) {
|
||||||
|
if (sourceDominant) {
|
||||||
|
builder.connection(src);
|
||||||
|
builder.location("connection", source.getLocation("connection"));
|
||||||
|
} else if (target.getConnection() == null) {
|
||||||
|
builder.connection(extrapolateChildUrl(src, source.isChildScmConnectionInheritAppendPath(), context));
|
||||||
|
builder.location("connection", source.getLocation("connection"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeScm_DeveloperConnection(
|
||||||
|
Scm.Builder builder, Scm target, Scm source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
String src = source.getDeveloperConnection();
|
||||||
|
if (src != null) {
|
||||||
|
if (sourceDominant) {
|
||||||
|
builder.developerConnection(src);
|
||||||
|
builder.location("developerConnection", source.getLocation("developerConnection"));
|
||||||
|
} else if (target.getDeveloperConnection() == null) {
|
||||||
|
String e = extrapolateChildUrl(src, source.isChildScmDeveloperConnectionInheritAppendPath(), context);
|
||||||
|
builder.developerConnection(e);
|
||||||
|
builder.location("developerConnection", source.getLocation("developerConnection"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergePlugin_Executions(
|
||||||
|
Plugin.Builder builder, Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) {
|
||||||
|
List<PluginExecution> src = source.getExecutions();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<PluginExecution> tgt = target.getExecutions();
|
||||||
|
Map<Object, PluginExecution> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
|
||||||
|
|
||||||
|
for (PluginExecution element : src) {
|
||||||
|
if (sourceDominant || (element.getInherited() != null ? element.isInherited() : source.isInherited())) {
|
||||||
|
Object key = getPluginExecutionKey().apply(element);
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PluginExecution element : tgt) {
|
||||||
|
Object key = getPluginExecutionKey().apply(element);
|
||||||
|
PluginExecution existing = merged.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
element = mergePluginExecution(element, existing, sourceDominant, context);
|
||||||
|
}
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.executions(merged.values());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergePluginExecution_Goals(
|
||||||
|
PluginExecution.Builder builder,
|
||||||
|
PluginExecution target,
|
||||||
|
PluginExecution source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<String> src = source.getGoals();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<String> tgt = target.getGoals();
|
||||||
|
Set<String> excludes = new LinkedHashSet<>(tgt);
|
||||||
|
List<String> merged = new ArrayList<>(tgt.size() + src.size());
|
||||||
|
merged.addAll(tgt);
|
||||||
|
for (String s : src) {
|
||||||
|
if (!excludes.contains(s)) {
|
||||||
|
merged.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.goals(merged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mergeReportPlugin_ReportSets(
|
||||||
|
ReportPlugin.Builder builder,
|
||||||
|
ReportPlugin target,
|
||||||
|
ReportPlugin source,
|
||||||
|
boolean sourceDominant,
|
||||||
|
Map<Object, Object> context) {
|
||||||
|
List<ReportSet> src = source.getReportSets();
|
||||||
|
if (!src.isEmpty()) {
|
||||||
|
List<ReportSet> tgt = target.getReportSets();
|
||||||
|
Map<Object, ReportSet> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
|
||||||
|
|
||||||
|
for (ReportSet rset : src) {
|
||||||
|
if (sourceDominant || (rset.getInherited() != null ? rset.isInherited() : source.isInherited())) {
|
||||||
|
Object key = getReportSetKey().apply(rset);
|
||||||
|
merged.put(key, rset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ReportSet element : tgt) {
|
||||||
|
Object key = getReportSetKey().apply(element);
|
||||||
|
ReportSet existing = merged.get(key);
|
||||||
|
if (existing != null) {
|
||||||
|
mergeReportSet(element, existing, sourceDominant, context);
|
||||||
|
}
|
||||||
|
merged.put(key, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.reportSets(merged.values());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KeyComputer<Dependency> getDependencyKey() {
|
||||||
|
return Dependency::getManagementKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KeyComputer<Plugin> getPluginKey() {
|
||||||
|
return Plugin::getKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KeyComputer<PluginExecution> getPluginExecutionKey() {
|
||||||
|
return PluginExecution::getId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KeyComputer<ReportPlugin> getReportPluginKey() {
|
||||||
|
return ReportPlugin::getKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KeyComputer<ReportSet> getReportSetKey() {
|
||||||
|
return ReportSet::getId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KeyComputer<RepositoryBase> getRepositoryBaseKey() {
|
||||||
|
return RepositoryBase::getId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KeyComputer<Extension> getExtensionKey() {
|
||||||
|
return e -> e.getGroupId() + ':' + e.getArtifactId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KeyComputer<Exclusion> getExclusionKey() {
|
||||||
|
return e -> e.getGroupId() + ':' + e.getArtifactId();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String extrapolateChildUrl(String parentUrl, boolean appendPath, Map<Object, Object> context) {
|
||||||
|
return parentUrl;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,33 +16,29 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.maven.internal.impl;
|
package org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.apache.maven.api.model.Model;
|
import org.apache.maven.api.model.Model;
|
||||||
import org.apache.maven.api.services.SuperPomProvider;
|
import org.apache.maven.api.services.ModelSource;
|
||||||
import org.apache.maven.api.services.SuperPomProviderException;
|
|
||||||
|
|
||||||
@Named
|
/**
|
||||||
@Singleton
|
* Holds a model along with some auxiliary information. This internal utility class assists the model builder during POM
|
||||||
public class DefaultSuperPomProvider implements SuperPomProvider {
|
* processing by providing a means to transport information that cannot be (easily) extracted from the model itself.
|
||||||
|
*/
|
||||||
|
record ModelData(ModelSource source, Model model) {
|
||||||
|
|
||||||
private final org.apache.maven.model.superpom.SuperPomProvider provider;
|
/**
|
||||||
|
* Gets unique identifier of the model
|
||||||
@Inject
|
*
|
||||||
public DefaultSuperPomProvider(org.apache.maven.model.superpom.SuperPomProvider provider) {
|
* @return The effective identifier of the model, never {@code null}.
|
||||||
this.provider = provider;
|
*/
|
||||||
|
public String id() {
|
||||||
|
// if source is null, it is the super model, which can be accessed via empty string
|
||||||
|
return source != null ? source.getLocation() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Model getSuperPom(String version) {
|
public String toString() {
|
||||||
try {
|
return String.valueOf(model);
|
||||||
return provider.getSuperModel(version).getDelegate();
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
throw new SuperPomProviderException("Could not retrieve super pom " + version, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.ModelProblem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assists in the handling of model problems.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ModelProblemUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a user-friendly source hint for the specified model.
|
||||||
|
*
|
||||||
|
* @param model The model to create a source hint for, may be {@code null}.
|
||||||
|
* @return The user-friendly source hint, never {@code null}.
|
||||||
|
*/
|
||||||
|
static String toSourceHint(Model model) {
|
||||||
|
if (model == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
StringBuilder buffer = new StringBuilder(128);
|
||||||
|
buffer.append(toId(model));
|
||||||
|
Path pomPath = model.getPomFile();
|
||||||
|
if (pomPath != null) {
|
||||||
|
buffer.append(" (").append(pomPath).append(')');
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toPath(Model model) {
|
||||||
|
String path = "";
|
||||||
|
if (model != null) {
|
||||||
|
Path pomPath = model.getPomFile();
|
||||||
|
if (pomPath != null) {
|
||||||
|
path = pomPath.toAbsolutePath().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toId(Model model) {
|
||||||
|
if (model == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String groupId = model.getGroupId();
|
||||||
|
if (groupId == null && model.getParent() != null) {
|
||||||
|
groupId = model.getParent().getGroupId();
|
||||||
|
}
|
||||||
|
String artifactId = model.getArtifactId();
|
||||||
|
String version = model.getVersion();
|
||||||
|
if (version == null && model.getParent() != null) {
|
||||||
|
version = model.getParent().getVersion();
|
||||||
|
}
|
||||||
|
return toId(groupId, artifactId, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a user-friendly artifact id from the specified coordinates.
|
||||||
|
*
|
||||||
|
* @param groupId The group id, may be {@code null}.
|
||||||
|
* @param artifactId The artifact id, may be {@code null}.
|
||||||
|
* @param version The version, may be {@code null}.
|
||||||
|
* @return The user-friendly artifact id, never {@code null}.
|
||||||
|
*/
|
||||||
|
static String toId(String groupId, String artifactId, String version) {
|
||||||
|
return ((groupId != null && !groupId.isEmpty()) ? groupId : "[unknown-group-id]")
|
||||||
|
+ ':'
|
||||||
|
+ ((artifactId != null && !artifactId.isEmpty()) ? artifactId : "[unknown-artifact-id]")
|
||||||
|
+ ':'
|
||||||
|
+ ((version != null && !version.isEmpty()) ? version : "[unknown-version]");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a string with all location details for the specified model problem. If the project identifier is
|
||||||
|
* provided, the generated location will omit the model id and source information and only give line/column
|
||||||
|
* information for problems originating directly from this POM.
|
||||||
|
*
|
||||||
|
* @param problem The problem whose location should be formatted, must not be {@code null}.
|
||||||
|
* @param projectId The {@code <groupId>:<artifactId>:<version>} of the corresponding project, may be {@code null}
|
||||||
|
* to force output of model id and source.
|
||||||
|
* @return The formatted problem location or an empty string if unknown, never {@code null}.
|
||||||
|
*/
|
||||||
|
public static String formatLocation(ModelProblem problem, String projectId) {
|
||||||
|
StringBuilder buffer = new StringBuilder(256);
|
||||||
|
|
||||||
|
if (!problem.getModelId().equals(projectId)) {
|
||||||
|
buffer.append(problem.getModelId());
|
||||||
|
if (!problem.getSource().isEmpty()) {
|
||||||
|
if (!buffer.isEmpty()) {
|
||||||
|
buffer.append(", ");
|
||||||
|
}
|
||||||
|
buffer.append(problem.getSource());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (problem.getLineNumber() > 0) {
|
||||||
|
if (!buffer.isEmpty()) {
|
||||||
|
buffer.append(", ");
|
||||||
|
}
|
||||||
|
buffer.append("line ").append(problem.getLineNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (problem.getColumnNumber() > 0) {
|
||||||
|
if (!buffer.isEmpty()) {
|
||||||
|
buffer.append(", ");
|
||||||
|
}
|
||||||
|
buffer.append("column ").append(problem.getColumnNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Inject;
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.ActivationFile;
|
||||||
|
import org.apache.maven.api.services.model.PathTranslator;
|
||||||
|
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||||
|
import org.apache.maven.api.services.model.RootLocator;
|
||||||
|
import org.codehaus.plexus.interpolation.AbstractValueSource;
|
||||||
|
import org.codehaus.plexus.interpolation.InterpolationException;
|
||||||
|
import org.codehaus.plexus.interpolation.MapBasedValueSource;
|
||||||
|
import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds an absolute path for {@link ActivationFile#getExists()} or {@link ActivationFile#getMissing()}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
public class ProfileActivationFilePathInterpolator {
|
||||||
|
|
||||||
|
private final PathTranslator pathTranslator;
|
||||||
|
|
||||||
|
private final RootLocator rootLocator;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ProfileActivationFilePathInterpolator(PathTranslator pathTranslator, RootLocator rootLocator) {
|
||||||
|
this.pathTranslator = pathTranslator;
|
||||||
|
this.rootLocator = rootLocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interpolates given {@code path}.
|
||||||
|
*
|
||||||
|
* @return absolute path or {@code null} if the input was {@code null}
|
||||||
|
*/
|
||||||
|
public String interpolate(String path, ProfileActivationContext context) throws InterpolationException {
|
||||||
|
if (path == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
|
||||||
|
|
||||||
|
Path basedir = context.getProjectDirectory();
|
||||||
|
|
||||||
|
if (basedir != null) {
|
||||||
|
interpolator.addValueSource(new AbstractValueSource(false) {
|
||||||
|
@Override
|
||||||
|
public Object getValue(String expression) {
|
||||||
|
if ("basedir".equals(expression) || "project.basedir".equals(expression)) {
|
||||||
|
return basedir.toAbsolutePath().toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (path.contains("${basedir}")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interpolator.addValueSource(new AbstractValueSource(false) {
|
||||||
|
@Override
|
||||||
|
public Object getValue(String expression) {
|
||||||
|
if ("rootDirectory".equals(expression)) {
|
||||||
|
Path root = rootLocator.findMandatoryRoot(basedir);
|
||||||
|
return root.toFile().getAbsolutePath();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
interpolator.addValueSource(new MapBasedValueSource(context.getProjectProperties()));
|
||||||
|
interpolator.addValueSource(new MapBasedValueSource(context.getUserProperties()));
|
||||||
|
interpolator.addValueSource(new MapBasedValueSource(context.getSystemProperties()));
|
||||||
|
|
||||||
|
String absolutePath = interpolator.interpolate(path, "");
|
||||||
|
|
||||||
|
return pathTranslator.alignToBaseDirectory(absolutePath, basedir);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,230 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.maven.api.services.BuilderProblem.Severity;
|
||||||
|
import org.apache.maven.api.services.ModelProblem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There are various forms of results that are represented by this class:
|
||||||
|
* <ol>
|
||||||
|
* <li>success - in which case only the model field is set
|
||||||
|
* <li>success with warnings - model field + non-error model problems
|
||||||
|
* <li>error - no model, but diagnostics
|
||||||
|
* <li>error - (partial) model and diagnostics
|
||||||
|
* </ol>
|
||||||
|
* Could encode these variants as subclasses, but kept in one for now
|
||||||
|
*
|
||||||
|
* @param <T> the model type
|
||||||
|
*/
|
||||||
|
public class Result<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Success without warnings
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> success(T model) {
|
||||||
|
return success(model, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Success with warnings
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
* @param problems
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> success(T model, Iterable<? extends ModelProblem> problems) {
|
||||||
|
assert !hasErrors(problems);
|
||||||
|
return new Result<>(false, model, problems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Success with warnings
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
* @param results
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> success(T model, Result<?>... results) {
|
||||||
|
final List<ModelProblem> problemsList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Result<?> result1 : results) {
|
||||||
|
for (ModelProblem modelProblem : result1.getProblems()) {
|
||||||
|
problemsList.add(modelProblem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success(model, problemsList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error with problems describing the cause
|
||||||
|
*
|
||||||
|
* @param problems
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> error(Iterable<? extends ModelProblem> problems) {
|
||||||
|
return error(null, problems);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> error(T model) {
|
||||||
|
return error(model, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> error(Result<?> result) {
|
||||||
|
return error(result.getProblems());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> error(Result<?>... results) {
|
||||||
|
final List<ModelProblem> problemsList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Result<?> result1 : results) {
|
||||||
|
for (ModelProblem modelProblem : result1.getProblems()) {
|
||||||
|
problemsList.add(modelProblem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return error(problemsList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error with partial result and problems describing the cause
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
* @param problems
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> error(T model, Iterable<? extends ModelProblem> problems) {
|
||||||
|
return new Result<>(true, model, problems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New result - determine whether error or success by checking problems for errors
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
* @param problems
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> newResult(T model, Iterable<? extends ModelProblem> problems) {
|
||||||
|
return new Result<>(hasErrors(problems), model, problems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New result consisting of given result and new problem. Convenience for newResult(result.get(),
|
||||||
|
* concat(result.getProblems(),problems)).
|
||||||
|
*
|
||||||
|
* @param result
|
||||||
|
* @param problem
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> addProblem(Result<T> result, ModelProblem problem) {
|
||||||
|
return addProblems(result, Collections.singleton(problem));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New result that includes the given
|
||||||
|
*
|
||||||
|
* @param result
|
||||||
|
* @param problems
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> addProblems(Result<T> result, Iterable<? extends ModelProblem> problems) {
|
||||||
|
Collection<ModelProblem> list = new ArrayList<>();
|
||||||
|
for (ModelProblem item : problems) {
|
||||||
|
list.add(item);
|
||||||
|
}
|
||||||
|
for (ModelProblem item : result.getProblems()) {
|
||||||
|
list.add(item);
|
||||||
|
}
|
||||||
|
return new Result<>(result.hasErrors() || hasErrors(problems), result.get(), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> addProblems(Result<T> result, Result<?>... results) {
|
||||||
|
final List<ModelProblem> problemsList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Result<?> result1 : results) {
|
||||||
|
for (ModelProblem modelProblem : result1.getProblems()) {
|
||||||
|
problemsList.add(modelProblem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addProblems(result, problemsList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns the given results into a single result by combining problems and models into single collection.
|
||||||
|
*
|
||||||
|
* @param results
|
||||||
|
*/
|
||||||
|
public static <T> Result<Iterable<T>> newResultSet(Iterable<? extends Result<? extends T>> results) {
|
||||||
|
boolean hasErrors = false;
|
||||||
|
List<T> modelsList = new ArrayList<>();
|
||||||
|
List<ModelProblem> problemsList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Result<? extends T> result : results) {
|
||||||
|
modelsList.add(result.get());
|
||||||
|
|
||||||
|
for (ModelProblem modelProblem : result.getProblems()) {
|
||||||
|
problemsList.add(modelProblem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.hasErrors()) {
|
||||||
|
hasErrors = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Result<>(hasErrors, (Iterable<T>) modelsList, problemsList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper to determine if problems contain error
|
||||||
|
private static boolean hasErrors(Iterable<? extends ModelProblem> problems) {
|
||||||
|
for (ModelProblem input : problems) {
|
||||||
|
if (input.getSeverity().equals(Severity.ERROR)
|
||||||
|
|| input.getSeverity().equals(Severity.FATAL)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class definition
|
||||||
|
*/
|
||||||
|
private final boolean errors;
|
||||||
|
|
||||||
|
private final T value;
|
||||||
|
|
||||||
|
private final Iterable<? extends ModelProblem> problems;
|
||||||
|
|
||||||
|
private Result(boolean errors, T model, Iterable<? extends ModelProblem> problems) {
|
||||||
|
this.errors = errors;
|
||||||
|
this.value = model;
|
||||||
|
this.problems = problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<? extends ModelProblem> getProblems() {
|
||||||
|
return problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasErrors() {
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model.profile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Inject;
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Activation;
|
||||||
|
import org.apache.maven.api.model.ActivationFile;
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
import org.apache.maven.api.services.BuilderProblem;
|
||||||
|
import org.apache.maven.api.services.ModelProblem;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||||
|
import org.apache.maven.api.services.model.ProfileActivator;
|
||||||
|
import org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
|
||||||
|
import org.codehaus.plexus.interpolation.InterpolationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines profile activation based on the existence/absence of some file.
|
||||||
|
* File name interpolation support is limited to <code>${project.basedir}</code>
|
||||||
|
* system properties and user properties.
|
||||||
|
*
|
||||||
|
* @see ActivationFile
|
||||||
|
* @see org.apache.maven.internal.impl.model.DefaultModelValidator#validateRawModel
|
||||||
|
*/
|
||||||
|
@Named("file")
|
||||||
|
@Singleton
|
||||||
|
public class FileProfileActivator implements ProfileActivator {
|
||||||
|
|
||||||
|
private final ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public FileProfileActivator(ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator) {
|
||||||
|
this.profileActivationFilePathInterpolator = profileActivationFilePathInterpolator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||||
|
Activation activation = profile.getActivation();
|
||||||
|
|
||||||
|
if (activation == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActivationFile file = activation.getFile();
|
||||||
|
|
||||||
|
if (file == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String path;
|
||||||
|
boolean missing;
|
||||||
|
|
||||||
|
if (file.getExists() != null && !file.getExists().isEmpty()) {
|
||||||
|
path = file.getExists();
|
||||||
|
missing = false;
|
||||||
|
} else if (file.getMissing() != null && !file.getMissing().isEmpty()) {
|
||||||
|
path = file.getMissing();
|
||||||
|
missing = true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
path = profileActivationFilePathInterpolator.interpolate(path, context);
|
||||||
|
} catch (InterpolationException e) {
|
||||||
|
problems.add(
|
||||||
|
BuilderProblem.Severity.ERROR,
|
||||||
|
ModelProblem.Version.BASE,
|
||||||
|
"Failed to interpolate file location " + path + " for profile " + profile.getId() + ": "
|
||||||
|
+ e.getMessage(),
|
||||||
|
file.getLocation(missing ? "missing" : "exists"),
|
||||||
|
e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
File f = new File(path);
|
||||||
|
|
||||||
|
if (!f.isAbsolute()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean fileExists = f.exists();
|
||||||
|
|
||||||
|
return missing != fileExists;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean presentInConfig(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||||
|
Activation activation = profile.getActivation();
|
||||||
|
|
||||||
|
if (activation == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActivationFile file = activation.getFile();
|
||||||
|
|
||||||
|
return file != null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model.profile;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Activation;
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
import org.apache.maven.api.services.BuilderProblem;
|
||||||
|
import org.apache.maven.api.services.ModelProblem;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||||
|
import org.apache.maven.api.services.model.ProfileActivator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines profile activation based on the version of the current Java runtime.
|
||||||
|
*
|
||||||
|
* @see Activation#getJdk()
|
||||||
|
*/
|
||||||
|
@Named("jdk-version")
|
||||||
|
@Singleton
|
||||||
|
public class JdkVersionProfileActivator implements ProfileActivator {
|
||||||
|
|
||||||
|
private static final Pattern FILTER_1 = Pattern.compile("[^\\d._-]");
|
||||||
|
private static final Pattern FILTER_2 = Pattern.compile("[._-]");
|
||||||
|
private static final Pattern FILTER_3 = Pattern.compile("\\."); // used for split now
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||||
|
Activation activation = profile.getActivation();
|
||||||
|
|
||||||
|
if (activation == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String jdk = activation.getJdk();
|
||||||
|
|
||||||
|
if (jdk == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String version = context.getSystemProperties().get("java.version");
|
||||||
|
|
||||||
|
if (version == null || version.isEmpty()) {
|
||||||
|
problems.add(
|
||||||
|
BuilderProblem.Severity.ERROR,
|
||||||
|
ModelProblem.Version.BASE,
|
||||||
|
"Failed to determine Java version for profile " + profile.getId(),
|
||||||
|
activation.getLocation("jdk"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isJavaVersionCompatible(jdk, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isJavaVersionCompatible(String requiredJdkRange, String currentJavaVersion) {
|
||||||
|
if (requiredJdkRange.startsWith("!")) {
|
||||||
|
return !currentJavaVersion.startsWith(requiredJdkRange.substring(1));
|
||||||
|
} else if (isRange(requiredJdkRange)) {
|
||||||
|
return isInRange(currentJavaVersion, getRange(requiredJdkRange));
|
||||||
|
} else {
|
||||||
|
return currentJavaVersion.startsWith(requiredJdkRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean presentInConfig(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||||
|
Activation activation = profile.getActivation();
|
||||||
|
|
||||||
|
if (activation == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String jdk = activation.getJdk();
|
||||||
|
|
||||||
|
return jdk != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isInRange(String value, List<RangeValue> range) {
|
||||||
|
int leftRelation = getRelationOrder(value, range.get(0), true);
|
||||||
|
|
||||||
|
if (leftRelation == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftRelation < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getRelationOrder(value, range.get(1), false) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getRelationOrder(String value, RangeValue rangeValue, boolean isLeft) {
|
||||||
|
if (rangeValue.value.isEmpty()) {
|
||||||
|
return isLeft ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = FILTER_1.matcher(value).replaceAll("");
|
||||||
|
|
||||||
|
List<String> valueTokens = new ArrayList<>(Arrays.asList(FILTER_2.split(value)));
|
||||||
|
List<String> rangeValueTokens = new ArrayList<>(Arrays.asList(FILTER_3.split(rangeValue.value)));
|
||||||
|
|
||||||
|
addZeroTokens(valueTokens, 3);
|
||||||
|
addZeroTokens(rangeValueTokens, 3);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
int x = Integer.parseInt(valueTokens.get(i));
|
||||||
|
int y = Integer.parseInt(rangeValueTokens.get(i));
|
||||||
|
if (x < y) {
|
||||||
|
return -1;
|
||||||
|
} else if (x > y) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!rangeValue.closed) {
|
||||||
|
return isLeft ? -1 : 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addZeroTokens(List<String> tokens, int max) {
|
||||||
|
while (tokens.size() < max) {
|
||||||
|
tokens.add("0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isRange(String value) {
|
||||||
|
return value.startsWith("[") || value.startsWith("(");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<RangeValue> getRange(String range) {
|
||||||
|
List<RangeValue> ranges = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String token : range.split(",")) {
|
||||||
|
if (token.startsWith("[")) {
|
||||||
|
ranges.add(new RangeValue(token.replace("[", ""), true));
|
||||||
|
} else if (token.startsWith("(")) {
|
||||||
|
ranges.add(new RangeValue(token.replace("(", ""), false));
|
||||||
|
} else if (token.endsWith("]")) {
|
||||||
|
ranges.add(new RangeValue(token.replace("]", ""), true));
|
||||||
|
} else if (token.endsWith(")")) {
|
||||||
|
ranges.add(new RangeValue(token.replace(")", ""), false));
|
||||||
|
} else if (token.isEmpty()) {
|
||||||
|
ranges.add(new RangeValue("", false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ranges.size() < 2) {
|
||||||
|
ranges.add(new RangeValue("99999999", false));
|
||||||
|
}
|
||||||
|
return ranges;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RangeValue {
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
private boolean closed;
|
||||||
|
|
||||||
|
RangeValue(String value, boolean closed) {
|
||||||
|
this.value = value.trim();
|
||||||
|
this.closed = closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model.profile;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Activation;
|
||||||
|
import org.apache.maven.api.model.ActivationOS;
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||||
|
import org.apache.maven.api.services.model.ProfileActivator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines profile activation based on the operating system of the current runtime platform.
|
||||||
|
*
|
||||||
|
* @see ActivationOS
|
||||||
|
*/
|
||||||
|
@Named("os")
|
||||||
|
@Singleton
|
||||||
|
public class OperatingSystemProfileActivator implements ProfileActivator {
|
||||||
|
|
||||||
|
private static final String REGEX_PREFIX = "regex:";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||||
|
Activation activation = profile.getActivation();
|
||||||
|
|
||||||
|
if (activation == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActivationOS os = activation.getOs();
|
||||||
|
|
||||||
|
if (os == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean active = ensureAtLeastOneNonNull(os);
|
||||||
|
|
||||||
|
String actualOsName = context.getSystemProperties().get("os.name").toLowerCase(Locale.ENGLISH);
|
||||||
|
String actualOsArch = context.getSystemProperties().get("os.arch").toLowerCase(Locale.ENGLISH);
|
||||||
|
String actualOsVersion = context.getSystemProperties().get("os.version").toLowerCase(Locale.ENGLISH);
|
||||||
|
|
||||||
|
if (active && os.getFamily() != null) {
|
||||||
|
active = determineFamilyMatch(os.getFamily(), actualOsName);
|
||||||
|
}
|
||||||
|
if (active && os.getName() != null) {
|
||||||
|
active = determineNameMatch(os.getName(), actualOsName);
|
||||||
|
}
|
||||||
|
if (active && os.getArch() != null) {
|
||||||
|
active = determineArchMatch(os.getArch(), actualOsArch);
|
||||||
|
}
|
||||||
|
if (active && os.getVersion() != null) {
|
||||||
|
active = determineVersionMatch(os.getVersion(), actualOsVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean presentInConfig(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||||
|
Activation activation = profile.getActivation();
|
||||||
|
|
||||||
|
if (activation == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActivationOS os = activation.getOs();
|
||||||
|
|
||||||
|
return os != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean ensureAtLeastOneNonNull(ActivationOS os) {
|
||||||
|
return os.getArch() != null || os.getFamily() != null || os.getName() != null || os.getVersion() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean determineVersionMatch(String expectedVersion, String actualVersion) {
|
||||||
|
String test = expectedVersion;
|
||||||
|
boolean reverse = false;
|
||||||
|
final boolean result;
|
||||||
|
if (test.startsWith(REGEX_PREFIX)) {
|
||||||
|
result = actualVersion.matches(test.substring(REGEX_PREFIX.length()));
|
||||||
|
} else {
|
||||||
|
if (test.startsWith("!")) {
|
||||||
|
reverse = true;
|
||||||
|
test = test.substring(1);
|
||||||
|
}
|
||||||
|
result = actualVersion.equals(test);
|
||||||
|
}
|
||||||
|
|
||||||
|
return reverse != result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean determineArchMatch(String expectedArch, String actualArch) {
|
||||||
|
String test = expectedArch;
|
||||||
|
boolean reverse = false;
|
||||||
|
|
||||||
|
if (test.startsWith("!")) {
|
||||||
|
reverse = true;
|
||||||
|
test = test.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean result = actualArch.equals(test);
|
||||||
|
|
||||||
|
return reverse != result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean determineNameMatch(String expectedName, String actualName) {
|
||||||
|
String test = expectedName;
|
||||||
|
boolean reverse = false;
|
||||||
|
|
||||||
|
if (test.startsWith("!")) {
|
||||||
|
reverse = true;
|
||||||
|
test = test.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean result = actualName.equals(test);
|
||||||
|
|
||||||
|
return reverse != result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean determineFamilyMatch(String family, String actualName) {
|
||||||
|
String test = family;
|
||||||
|
boolean reverse = false;
|
||||||
|
|
||||||
|
if (test.startsWith("!")) {
|
||||||
|
reverse = true;
|
||||||
|
test = test.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean result = Os.isFamily(test, actualName);
|
||||||
|
|
||||||
|
return reverse != result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,233 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model.profile;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS support
|
||||||
|
*/
|
||||||
|
public class Os {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The OS Name.
|
||||||
|
*/
|
||||||
|
public static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The OA architecture.
|
||||||
|
*/
|
||||||
|
public static final String OS_ARCH = System.getProperty("os.arch").toLowerCase(Locale.ENGLISH);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The OS version.
|
||||||
|
*/
|
||||||
|
public static final String OS_VERSION = System.getProperty("os.version").toLowerCase(Locale.ENGLISH);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS Family
|
||||||
|
*/
|
||||||
|
public static final String OS_FAMILY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boolean indicating if the running OS is a Windows system.
|
||||||
|
*/
|
||||||
|
public static final boolean IS_WINDOWS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS family that can be tested for. {@value}
|
||||||
|
*/
|
||||||
|
private static final String FAMILY_WINDOWS = "windows";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS family that can be tested for. {@value}
|
||||||
|
*/
|
||||||
|
private static final String FAMILY_WIN9X = "win9x";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS family that can be tested for. {@value}
|
||||||
|
*/
|
||||||
|
public static final String FAMILY_NT = "winnt";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS family that can be tested for. {@value}
|
||||||
|
*/
|
||||||
|
private static final String FAMILY_OS2 = "os/2";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS family that can be tested for. {@value}
|
||||||
|
*/
|
||||||
|
private static final String FAMILY_NETWARE = "netware";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS family that can be tested for. {@value}
|
||||||
|
*/
|
||||||
|
private static final String FAMILY_DOS = "dos";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS family that can be tested for. {@value}
|
||||||
|
*/
|
||||||
|
private static final String FAMILY_MAC = "mac";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS family that can be tested for. {@value}
|
||||||
|
*/
|
||||||
|
private static final String FAMILY_TANDEM = "tandem";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS family that can be tested for. {@value}
|
||||||
|
*/
|
||||||
|
private static final String FAMILY_UNIX = "unix";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS family that can be tested for. {@value}
|
||||||
|
*/
|
||||||
|
private static final String FAMILY_OPENVMS = "openvms";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS family that can be tested for. {@value}
|
||||||
|
*/
|
||||||
|
private static final String FAMILY_ZOS = "z/os";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS family that can be tested for. {@value}
|
||||||
|
*/
|
||||||
|
private static final String FAMILY_OS390 = "os/390";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OS family that can be tested for. {@value}
|
||||||
|
*/
|
||||||
|
private static final String FAMILY_OS400 = "os/400";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenJDK is reported to call MacOS X "Darwin"
|
||||||
|
*
|
||||||
|
* @see <a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=44889">bugzilla issue</a>
|
||||||
|
* @see <a href="https://issues.apache.org/jira/browse/HADOOP-3318">HADOOP-3318</a>
|
||||||
|
*/
|
||||||
|
private static final String DARWIN = "darwin";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path separator.
|
||||||
|
*/
|
||||||
|
private static final String PATH_SEP = System.getProperty("path.separator");
|
||||||
|
|
||||||
|
static {
|
||||||
|
// Those two public constants are initialized here, as they need all the private constants
|
||||||
|
// above to be initialized first, but the code style imposes the public constants to be
|
||||||
|
// defined above the private ones...
|
||||||
|
OS_FAMILY = getOsFamily();
|
||||||
|
IS_WINDOWS = isFamily(FAMILY_WINDOWS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Os() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the OS on which Maven is executing matches the
|
||||||
|
* given OS family.
|
||||||
|
*
|
||||||
|
* @param family the family to check for
|
||||||
|
* @return true if the OS matches
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static boolean isFamily(String family) {
|
||||||
|
return isFamily(family, OS_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the OS on which Maven is executing matches the
|
||||||
|
* given OS family derived from the given OS name
|
||||||
|
*
|
||||||
|
* @param family the family to check for
|
||||||
|
* @param actualOsName the OS name to check against
|
||||||
|
* @return true if the OS matches
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static boolean isFamily(String family, String actualOsName) {
|
||||||
|
// windows probing logic relies on the word 'windows' in the OS
|
||||||
|
boolean isWindows = actualOsName.contains(FAMILY_WINDOWS);
|
||||||
|
boolean is9x = false;
|
||||||
|
boolean isNT = false;
|
||||||
|
if (isWindows) {
|
||||||
|
// there are only four 9x platforms that we look for
|
||||||
|
is9x = (actualOsName.contains("95")
|
||||||
|
|| actualOsName.contains("98")
|
||||||
|
|| actualOsName.contains("me")
|
||||||
|
// wince isn't really 9x, but crippled enough to
|
||||||
|
// be a muchness. Maven doesnt run on CE, anyway.
|
||||||
|
|| actualOsName.contains("ce"));
|
||||||
|
isNT = !is9x;
|
||||||
|
}
|
||||||
|
switch (family) {
|
||||||
|
case FAMILY_WINDOWS:
|
||||||
|
return isWindows;
|
||||||
|
case FAMILY_WIN9X:
|
||||||
|
return isWindows && is9x;
|
||||||
|
case FAMILY_NT:
|
||||||
|
return isWindows && isNT;
|
||||||
|
case FAMILY_OS2:
|
||||||
|
return actualOsName.contains(FAMILY_OS2);
|
||||||
|
case FAMILY_NETWARE:
|
||||||
|
return actualOsName.contains(FAMILY_NETWARE);
|
||||||
|
case FAMILY_DOS:
|
||||||
|
return PATH_SEP.equals(";") && !isFamily(FAMILY_NETWARE, actualOsName) && !isWindows;
|
||||||
|
case FAMILY_MAC:
|
||||||
|
return actualOsName.contains(FAMILY_MAC) || actualOsName.contains(DARWIN);
|
||||||
|
case FAMILY_TANDEM:
|
||||||
|
return actualOsName.contains("nonstop_kernel");
|
||||||
|
case FAMILY_UNIX:
|
||||||
|
return PATH_SEP.equals(":")
|
||||||
|
&& !isFamily(FAMILY_OPENVMS, actualOsName)
|
||||||
|
&& (!isFamily(FAMILY_MAC, actualOsName) || actualOsName.endsWith("x"));
|
||||||
|
case FAMILY_ZOS:
|
||||||
|
return actualOsName.contains(FAMILY_ZOS) || actualOsName.contains(FAMILY_OS390);
|
||||||
|
case FAMILY_OS400:
|
||||||
|
return actualOsName.contains(FAMILY_OS400);
|
||||||
|
case FAMILY_OPENVMS:
|
||||||
|
return actualOsName.contains(FAMILY_OPENVMS);
|
||||||
|
default:
|
||||||
|
return actualOsName.contains(family.toLowerCase(Locale.US));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to determine the current OS family.
|
||||||
|
*
|
||||||
|
* @return name of current OS family.
|
||||||
|
*/
|
||||||
|
private static String getOsFamily() {
|
||||||
|
return Stream.of(
|
||||||
|
FAMILY_DOS,
|
||||||
|
FAMILY_MAC,
|
||||||
|
FAMILY_NETWARE,
|
||||||
|
FAMILY_NT,
|
||||||
|
FAMILY_OPENVMS,
|
||||||
|
FAMILY_OS2,
|
||||||
|
FAMILY_OS400,
|
||||||
|
FAMILY_TANDEM,
|
||||||
|
FAMILY_UNIX,
|
||||||
|
FAMILY_WIN9X,
|
||||||
|
FAMILY_WINDOWS,
|
||||||
|
FAMILY_ZOS)
|
||||||
|
.filter(Os::isFamily)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model.profile;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Activation;
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||||
|
import org.apache.maven.api.services.model.ProfileActivator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines profile activation based on the project's packaging.
|
||||||
|
*/
|
||||||
|
@Named("packaging")
|
||||||
|
@Singleton
|
||||||
|
public class PackagingProfileActivator implements ProfileActivator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||||
|
return getActivationPackaging(profile).map(p -> isPackaging(context, p)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean presentInConfig(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||||
|
return getActivationPackaging(profile).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPackaging(ProfileActivationContext context, String p) {
|
||||||
|
String packaging = context.getUserProperties().get(ProfileActivationContext.PROPERTY_NAME_PACKAGING);
|
||||||
|
return Objects.equals(p, packaging);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<String> getActivationPackaging(Profile profile) {
|
||||||
|
return Optional.ofNullable(profile).map(Profile::getActivation).map(Activation::getPackaging);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.model.profile;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Named;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.model.Activation;
|
||||||
|
import org.apache.maven.api.model.ActivationProperty;
|
||||||
|
import org.apache.maven.api.model.Profile;
|
||||||
|
import org.apache.maven.api.services.BuilderProblem;
|
||||||
|
import org.apache.maven.api.services.ModelProblem;
|
||||||
|
import org.apache.maven.api.services.ModelProblemCollector;
|
||||||
|
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||||
|
import org.apache.maven.api.services.model.ProfileActivator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines profile activation based on the existence or value of some execution property.
|
||||||
|
*
|
||||||
|
* @see ActivationProperty
|
||||||
|
*/
|
||||||
|
@Named("property")
|
||||||
|
@Singleton
|
||||||
|
public class PropertyProfileActivator implements ProfileActivator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||||
|
Activation activation = profile.getActivation();
|
||||||
|
|
||||||
|
if (activation == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActivationProperty property = activation.getProperty();
|
||||||
|
|
||||||
|
if (property == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = property.getName();
|
||||||
|
boolean reverseName = false;
|
||||||
|
|
||||||
|
if (name != null && name.startsWith("!")) {
|
||||||
|
reverseName = true;
|
||||||
|
name = name.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name == null || name.isEmpty()) {
|
||||||
|
problems.add(
|
||||||
|
BuilderProblem.Severity.ERROR,
|
||||||
|
ModelProblem.Version.BASE,
|
||||||
|
"The property name is required to activate the profile " + profile.getId(),
|
||||||
|
property.getLocation(""));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String sysValue = context.getUserProperties().get(name);
|
||||||
|
if (sysValue == null) {
|
||||||
|
sysValue = context.getSystemProperties().get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
String propValue = property.getValue();
|
||||||
|
if (propValue != null && !propValue.isEmpty()) {
|
||||||
|
boolean reverseValue = false;
|
||||||
|
if (propValue.startsWith("!")) {
|
||||||
|
reverseValue = true;
|
||||||
|
propValue = propValue.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have a value, so it has to match the system value...
|
||||||
|
return reverseValue != propValue.equals(sysValue);
|
||||||
|
} else {
|
||||||
|
return reverseName != (sysValue != null && !sysValue.isEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean presentInConfig(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||||
|
Activation activation = profile.getActivation();
|
||||||
|
|
||||||
|
if (activation == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActivationProperty property = activation.getProperty();
|
||||||
|
|
||||||
|
return property != null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.maven.internal.impl.resolver;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.maven.api.Language;
|
||||||
|
import org.apache.maven.api.model.DependencyManagement;
|
||||||
|
import org.apache.maven.api.model.DistributionManagement;
|
||||||
|
import org.apache.maven.api.model.License;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.model.Prerequisites;
|
||||||
|
import org.apache.maven.api.model.Repository;
|
||||||
|
import org.apache.maven.api.services.RepositoryFactory;
|
||||||
|
import org.apache.maven.internal.impl.InternalSession;
|
||||||
|
import org.apache.maven.internal.impl.resolver.artifact.MavenArtifactProperties;
|
||||||
|
import org.apache.maven.internal.impl.resolver.type.DefaultType;
|
||||||
|
import org.eclipse.aether.artifact.Artifact;
|
||||||
|
import org.eclipse.aether.artifact.ArtifactProperties;
|
||||||
|
import org.eclipse.aether.artifact.ArtifactType;
|
||||||
|
import org.eclipse.aether.artifact.ArtifactTypeRegistry;
|
||||||
|
import org.eclipse.aether.artifact.DefaultArtifact;
|
||||||
|
import org.eclipse.aether.graph.Dependency;
|
||||||
|
import org.eclipse.aether.graph.Exclusion;
|
||||||
|
import org.eclipse.aether.resolution.ArtifactDescriptorResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates Aether {@link ArtifactDescriptorResult} from Maven project {@link Model}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Note:</strong> This class is part of work in progress and can be changed or removed without notice.
|
||||||
|
* @since 3.2.4
|
||||||
|
*/
|
||||||
|
public class ArtifactDescriptorReaderDelegate {
|
||||||
|
public void populateResult(InternalSession session, ArtifactDescriptorResult result, Model model) {
|
||||||
|
ArtifactTypeRegistry stereotypes = session.getSession().getArtifactTypeRegistry();
|
||||||
|
|
||||||
|
for (Repository r : model.getRepositories()) {
|
||||||
|
result.addRepository(session.toRepository(
|
||||||
|
session.getService(RepositoryFactory.class).createRemote(r)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (org.apache.maven.api.model.Dependency dependency : model.getDependencies()) {
|
||||||
|
result.addDependency(convert(dependency, stereotypes));
|
||||||
|
}
|
||||||
|
|
||||||
|
DependencyManagement mgmt = model.getDependencyManagement();
|
||||||
|
if (mgmt != null) {
|
||||||
|
for (org.apache.maven.api.model.Dependency dependency : mgmt.getDependencies()) {
|
||||||
|
result.addManagedDependency(convert(dependency, stereotypes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> properties = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
Prerequisites prerequisites = model.getPrerequisites();
|
||||||
|
if (prerequisites != null) {
|
||||||
|
properties.put("prerequisites.maven", prerequisites.getMaven());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<License> licenses = model.getLicenses();
|
||||||
|
properties.put("license.count", licenses.size());
|
||||||
|
for (int i = 0; i < licenses.size(); i++) {
|
||||||
|
License license = licenses.get(i);
|
||||||
|
properties.put("license." + i + ".name", license.getName());
|
||||||
|
properties.put("license." + i + ".url", license.getUrl());
|
||||||
|
properties.put("license." + i + ".comments", license.getComments());
|
||||||
|
properties.put("license." + i + ".distribution", license.getDistribution());
|
||||||
|
}
|
||||||
|
|
||||||
|
result.setProperties(properties);
|
||||||
|
|
||||||
|
setArtifactProperties(result, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dependency convert(org.apache.maven.api.model.Dependency dependency, ArtifactTypeRegistry stereotypes) {
|
||||||
|
ArtifactType stereotype = stereotypes.get(dependency.getType());
|
||||||
|
if (stereotype == null) {
|
||||||
|
// TODO: this here is fishy
|
||||||
|
stereotype = new DefaultType(dependency.getType(), Language.NONE, "", null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean system = dependency.getSystemPath() != null
|
||||||
|
&& !dependency.getSystemPath().isEmpty();
|
||||||
|
|
||||||
|
Map<String, String> props = null;
|
||||||
|
if (system) {
|
||||||
|
props = Collections.singletonMap(MavenArtifactProperties.LOCAL_PATH, dependency.getSystemPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
Artifact artifact = new DefaultArtifact(
|
||||||
|
dependency.getGroupId(),
|
||||||
|
dependency.getArtifactId(),
|
||||||
|
dependency.getClassifier(),
|
||||||
|
null,
|
||||||
|
dependency.getVersion(),
|
||||||
|
props,
|
||||||
|
stereotype);
|
||||||
|
|
||||||
|
List<Exclusion> exclusions = new ArrayList<>(dependency.getExclusions().size());
|
||||||
|
for (org.apache.maven.api.model.Exclusion exclusion : dependency.getExclusions()) {
|
||||||
|
exclusions.add(convert(exclusion));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Dependency(
|
||||||
|
artifact,
|
||||||
|
dependency.getScope(),
|
||||||
|
dependency.getOptional() != null ? dependency.isOptional() : null,
|
||||||
|
exclusions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Exclusion convert(org.apache.maven.api.model.Exclusion exclusion) {
|
||||||
|
return new Exclusion(exclusion.getGroupId(), exclusion.getArtifactId(), "*", "*");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setArtifactProperties(ArtifactDescriptorResult result, Model model) {
|
||||||
|
String downloadUrl = null;
|
||||||
|
DistributionManagement distMgmt = model.getDistributionManagement();
|
||||||
|
if (distMgmt != null) {
|
||||||
|
downloadUrl = distMgmt.getDownloadUrl();
|
||||||
|
}
|
||||||
|
if (downloadUrl != null && !downloadUrl.isEmpty()) {
|
||||||
|
Artifact artifact = result.getArtifact();
|
||||||
|
Map<String, String> props = new HashMap<>(artifact.getProperties());
|
||||||
|
props.put(ArtifactProperties.DOWNLOAD_URL, downloadUrl);
|
||||||
|
result.setArtifact(artifact.setProperties(props));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue