diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java index 70c9ed529c..94de1d18e5 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java @@ -24,6 +24,7 @@ import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; import org.apache.maven.artifact.versioning.VersionRange; import org.apache.maven.model.Activation; +import org.apache.maven.model.ActivationFile; import org.apache.maven.model.Build; import org.apache.maven.model.Dependency; import org.apache.maven.model.DependencyManagement; @@ -46,11 +47,13 @@ import org.apache.maven.model.management.PluginManagementInjector; import org.apache.maven.model.normalization.ModelNormalizer; import org.apache.maven.model.path.ModelPathTranslator; import org.apache.maven.model.path.ModelUrlNormalizer; +import org.apache.maven.model.path.ProfileActivationFilePathInterpolator; import org.apache.maven.model.plugin.LifecycleBindingsInjector; import org.apache.maven.model.plugin.PluginConfigurationExpander; import org.apache.maven.model.plugin.ReportConfigurationExpander; import org.apache.maven.model.plugin.ReportingConverter; import org.apache.maven.model.profile.DefaultProfileActivationContext; +import org.apache.maven.model.profile.ProfileActivationContext; import org.apache.maven.model.profile.ProfileInjector; import org.apache.maven.model.profile.ProfileSelector; import org.apache.maven.model.resolution.InvalidRepositoryException; @@ -59,8 +62,10 @@ import org.apache.maven.model.resolution.UnresolvableModelException; import org.apache.maven.model.resolution.WorkspaceModelResolver; import org.apache.maven.model.superpom.SuperPomProvider; import org.apache.maven.model.validation.ModelValidator; +import org.codehaus.plexus.interpolation.InterpolationException; import org.codehaus.plexus.interpolation.MapBasedValueSource; import org.codehaus.plexus.interpolation.StringSearchInterpolator; +import org.codehaus.plexus.util.StringUtils; import org.eclipse.sisu.Nullable; import java.io.File; @@ -142,6 +147,9 @@ public class DefaultModelBuilder @Inject private ReportingConverter reportingConverter; + @Inject + private ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator; + public DefaultModelBuilder setModelProcessor( ModelProcessor modelProcessor ) { this.modelProcessor = modelProcessor; @@ -244,6 +252,13 @@ public class DefaultModelBuilder return this; } + public DefaultModelBuilder setProfileActivationFilePathInterpolator( + ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator ) + { + this.profileActivationFilePathInterpolator = profileActivationFilePathInterpolator; + return this; + } + @SuppressWarnings( "checkstyle:methodlength" ) @Override public ModelBuildingResult build( ModelBuildingRequest request ) @@ -317,7 +332,9 @@ public class DefaultModelBuilder profileActivationContext, problems ); currentData.setActiveProfiles( activePomProfiles ); - Map interpolatedActivations = getProfileActivations( rawModel, false ); + Map interpolatedActivations = getInterpolatedActivations( rawModel, + profileActivationContext, + problems ); injectProfileActivations( tmpModel, interpolatedActivations ); // profile injection @@ -440,6 +457,51 @@ public class DefaultModelBuilder return result; } + private Map getInterpolatedActivations( Model rawModel, + DefaultProfileActivationContext context, + DefaultModelProblemCollector problems ) + { + Map interpolatedActivations = getProfileActivations( rawModel, true ); + for ( Activation activation : interpolatedActivations.values() ) + { + if ( activation.getFile() != null ) + { + replaceWithInterpolatedValue( activation.getFile(), context, problems ); + } + } + return interpolatedActivations; + } + + private void replaceWithInterpolatedValue( ActivationFile activationFile, ProfileActivationContext context, + DefaultModelProblemCollector problems ) + { + try + { + if ( StringUtils.isNotEmpty( activationFile.getExists() ) ) + { + String path = activationFile.getExists(); + String absolutePath = profileActivationFilePathInterpolator.interpolate( path, context ); + activationFile.setExists( absolutePath ); + } + else if ( StringUtils.isNotEmpty( activationFile.getMissing() ) ) + { + String path = activationFile.getMissing(); + String absolutePath = profileActivationFilePathInterpolator.interpolate( path, context ); + activationFile.setMissing( absolutePath ); + } + } + catch ( InterpolationException e ) + { + String path = StringUtils.isNotEmpty( + activationFile.getExists() ) ? activationFile.getExists() : activationFile.getMissing(); + + problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage( + "Failed to interpolate file location " + path + ": " + e.getMessage() ).setLocation( + activationFile.getLocation( StringUtils.isNotEmpty( activationFile.getExists() ) ? "exists" : "missing" ) ) + .setException( e ) ); + } + } + @Override public ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult result ) throws ModelBuildingException @@ -818,7 +880,7 @@ public class DefaultModelBuilder problems.add( mpcr ); } - + } interpolatedModel.setPomFile( model.getPomFile() ); diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java index 4240574ff2..730c5a29fe 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java @@ -43,6 +43,7 @@ import org.apache.maven.model.path.DefaultUrlNormalizer; import org.apache.maven.model.path.ModelPathTranslator; import org.apache.maven.model.path.ModelUrlNormalizer; import org.apache.maven.model.path.PathTranslator; +import org.apache.maven.model.path.ProfileActivationFilePathInterpolator; import org.apache.maven.model.path.UrlNormalizer; import org.apache.maven.model.plugin.DefaultPluginConfigurationExpander; import org.apache.maven.model.plugin.DefaultReportConfigurationExpander; @@ -109,7 +110,13 @@ public class DefaultModelBuilderFactory protected ProfileActivator[] newProfileActivators() { return new ProfileActivator[] { new JdkVersionProfileActivator(), new OperatingSystemProfileActivator(), - new PropertyProfileActivator(), new FileProfileActivator().setPathTranslator( newPathTranslator() ) }; + new PropertyProfileActivator(), new FileProfileActivator() + .setProfileActivationFilePathInterpolator( newProfileActivationFilePathInterpolator() ) }; + } + + protected ProfileActivationFilePathInterpolator newProfileActivationFilePathInterpolator() + { + return new ProfileActivationFilePathInterpolator().setPathTranslator( newPathTranslator() ); } protected UrlNormalizer newUrlNormalizer() @@ -225,6 +232,7 @@ public class DefaultModelBuilderFactory modelBuilder.setPluginConfigurationExpander( newPluginConfigurationExpander() ); modelBuilder.setReportConfigurationExpander( newReportConfigurationExpander() ); modelBuilder.setReportingConverter( newReportingConverter() ); + modelBuilder.setProfileActivationFilePathInterpolator( newProfileActivationFilePathInterpolator() ); return modelBuilder; } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/path/ProfileActivationFilePathInterpolator.java b/maven-model-builder/src/main/java/org/apache/maven/model/path/ProfileActivationFilePathInterpolator.java new file mode 100644 index 0000000000..c2f815b7f7 --- /dev/null +++ b/maven-model-builder/src/main/java/org/apache/maven/model/path/ProfileActivationFilePathInterpolator.java @@ -0,0 +1,103 @@ +package org.apache.maven.model.path; + +/* + * 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. + */ + +import org.apache.maven.model.ActivationFile; +import org.apache.maven.model.profile.ProfileActivationContext; +import org.codehaus.plexus.interpolation.AbstractValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import java.io.File; + +/** + * Finds an absolute path for {@link ActivationFile#getExists()} or {@link ActivationFile#getMissing()} + * + * @author Ravil Galeyev + */ +@Named +@Singleton +public class ProfileActivationFilePathInterpolator +{ + + @Inject + private PathTranslator pathTranslator; + + public ProfileActivationFilePathInterpolator setPathTranslator( PathTranslator pathTranslator ) + { + this.pathTranslator = pathTranslator; + return this; + } + + /** + * 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(); + + final File basedir = context.getProjectDirectory(); + + if ( basedir != null ) + { + interpolator.addValueSource( new AbstractValueSource( false ) + { + @Override + public Object getValue( String expression ) + { + /* + * We intentionally only support ${basedir} and not ${project.basedir} as the latter form + * would suggest that other project.* expressions can be used which is beyond the design. + */ + if ( "basedir".equals( expression ) ) + { + return basedir.getAbsolutePath(); + } + return null; + } + } ); + } + else if ( path.contains( "${basedir}" ) ) + { + 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 ); + } +} diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/activation/FileProfileActivator.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/activation/FileProfileActivator.java index abfa57edfc..923ffd2eb3 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/activation/FileProfileActivator.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/activation/FileProfileActivator.java @@ -28,15 +28,13 @@ import javax.inject.Singleton; import org.apache.maven.model.Activation; import org.apache.maven.model.ActivationFile; import org.apache.maven.model.Profile; -import org.apache.maven.model.building.ModelProblemCollector; import org.apache.maven.model.building.ModelProblem.Severity; import org.apache.maven.model.building.ModelProblem.Version; +import org.apache.maven.model.building.ModelProblemCollector; import org.apache.maven.model.building.ModelProblemCollectorRequest; -import org.apache.maven.model.path.PathTranslator; +import org.apache.maven.model.path.ProfileActivationFilePathInterpolator; import org.apache.maven.model.profile.ProfileActivationContext; -import org.codehaus.plexus.interpolation.AbstractValueSource; -import org.codehaus.plexus.interpolation.MapBasedValueSource; -import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.interpolation.InterpolationException; import org.codehaus.plexus.util.StringUtils; /** @@ -58,11 +56,12 @@ public class FileProfileActivator { @Inject - private PathTranslator pathTranslator; + private ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator; - public FileProfileActivator setPathTranslator( PathTranslator pathTranslator ) + public FileProfileActivator setProfileActivationFilePathInterpolator( + ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator ) { - this.pathTranslator = pathTranslator; + this.profileActivationFilePathInterpolator = profileActivationFilePathInterpolator; return this; } @@ -101,64 +100,23 @@ public class FileProfileActivator return false; } - RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); - - final File basedir = context.getProjectDirectory(); - - if ( basedir != null ) - { - interpolator.addValueSource( new AbstractValueSource( false ) - { - @Override - public Object getValue( String expression ) - { - /* - * NOTE: We intentionally only support ${basedir} and not ${project.basedir} as the latter form - * would suggest that other project.* expressions can be used which is however beyond the design. - */ - if ( "basedir".equals( expression ) ) - { - return basedir.getAbsolutePath(); - } - return null; - } - } ); - } - else if ( path.contains( "${basedir}" ) ) - { - return false; - } - - interpolator.addValueSource( new MapBasedValueSource( context.getProjectProperties() ) ); - - interpolator.addValueSource( new MapBasedValueSource( context.getUserProperties() ) ); - - interpolator.addValueSource( new MapBasedValueSource( context.getSystemProperties() ) ); - try { - path = interpolator.interpolate( path, "" ); + path = profileActivationFilePathInterpolator.interpolate( path, context ); } - catch ( Exception e ) + catch ( InterpolationException e ) { problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) .setMessage( "Failed to interpolate file location " + path + " for profile " + profile.getId() - + ": " + e.getMessage() ) + + ": " + e.getMessage() ) .setLocation( file.getLocation( missing ? "missing" : "exists" ) ) .setException( e ) ); return false; } - path = pathTranslator.alignToBaseDirectory( path, basedir ); - - // replace activation value with interpolated value - if ( missing ) + if ( path == null ) { - file.setMissing( path ); - } - else - { - file.setExists( path ); + return false; } File f = new File( path ); diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/building/DefaultModelBuilderFactoryTest.java b/maven-model-builder/src/test/java/org/apache/maven/model/building/DefaultModelBuilderFactoryTest.java index 90b65a4c92..32c7d268d5 100644 --- a/maven-model-builder/src/test/java/org/apache/maven/model/building/DefaultModelBuilderFactoryTest.java +++ b/maven-model-builder/src/test/java/org/apache/maven/model/building/DefaultModelBuilderFactoryTest.java @@ -20,11 +20,17 @@ package org.apache.maven.model.building; */ import java.io.File; +import java.io.FileInputStream; +import java.nio.file.Paths; +import org.apache.maven.model.Model; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; import org.codehaus.plexus.util.xml.Xpp3Dom; import junit.framework.TestCase; +import org.junit.Test; + /** * @author Benjamin Bentmann */ @@ -32,9 +38,11 @@ public class DefaultModelBuilderFactoryTest extends TestCase { + private static final String BASE_DIR = Paths.get( "src", "test", "resources", "poms", "factory" ).toString(); + private File getPom( String name ) { - return new File( "src/test/resources/poms/factory/" + name + ".xml" ).getAbsoluteFile(); + return new File( Paths.get( BASE_DIR, name + ".xml" ).toString() ).getAbsoluteFile(); } public void testCompleteWiring() @@ -56,4 +64,30 @@ public class DefaultModelBuilderFactoryTest assertEquals( " 1.5 ", conf.getChild( "target" ).getValue() ); } + @Test + public void testPomChanges() throws Exception + { + ModelBuilder builder = new DefaultModelBuilderFactory().newInstance(); + assertNotNull( builder ); + File pom = getPom( "simple" ); + + String originalExists = readPom( pom ).getProfiles().get( 1 ).getActivation().getFile().getExists(); + + DefaultModelBuildingRequest request = new DefaultModelBuildingRequest(); + request.setProcessPlugins( true ); + request.setPomFile( pom ); + ModelBuildingResult result = builder.build( request ); + String resultExists = result.getRawModel().getProfiles().get( 1 ).getActivation().getFile().getExists(); + + assertEquals( originalExists, resultExists ); + assertTrue( result.getEffectiveModel().getProfiles().get( 1 ).getActivation().getFile().getExists() + .contains( BASE_DIR ) ); + } + + private static Model readPom( File file ) throws Exception + { + MavenXpp3Reader reader = new MavenXpp3Reader(); + + return reader.read( new FileInputStream( file ) ); + } } diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/profile/activation/FileProfileActivatorTest.java b/maven-model-builder/src/test/java/org/apache/maven/model/profile/activation/FileProfileActivatorTest.java new file mode 100644 index 0000000000..c8b5c93b8d --- /dev/null +++ b/maven-model-builder/src/test/java/org/apache/maven/model/profile/activation/FileProfileActivatorTest.java @@ -0,0 +1,131 @@ +package org.apache.maven.model.profile.activation; + +/* + * 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. + */ + +import org.apache.maven.model.Activation; +import org.apache.maven.model.ActivationFile; +import org.apache.maven.model.Profile; +import org.apache.maven.model.path.DefaultPathTranslator; +import org.apache.maven.model.path.ProfileActivationFilePathInterpolator; +import org.apache.maven.model.profile.DefaultProfileActivationContext; + +import org.junit.Before; +import org.junit.Test; + +import java.nio.file.Files; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; + +/** + * Tests {@link FileProfileActivator}. + * + * @author Ravil Galeyev + */ +public class FileProfileActivatorTest extends AbstractProfileActivatorTest +{ + Path tempDir; + + private final DefaultProfileActivationContext context = new DefaultProfileActivationContext(); + + public FileProfileActivatorTest() + { + super( FileProfileActivator.class ); + } + + @Before + public void setUp() throws Exception + { + super.setUp(); + + tempDir = Files.createTempDirectory( null ); + + activator.setProfileActivationFilePathInterpolator( + new ProfileActivationFilePathInterpolator().setPathTranslator( new DefaultPathTranslator() ) ); + + context.setProjectDirectory( new File( tempDir.toString() ) ); + + File file = new File( tempDir.resolve( "file.txt" ).toString() ); + if ( !file.createNewFile() ) + { + throw new IOException( "Can't create " + file ); + } + } + + @Test + public void testIsActiveNoFile() + { + assertActivation( false, newExistsProfile( null ), context ); + assertActivation( false, newExistsProfile( "someFile.txt" ), context ); + assertActivation( false, newExistsProfile( "${basedir}/someFile.txt" ), context ); + + assertActivation( false, newMissingProfile( null ), context ); + assertActivation( true, newMissingProfile( "someFile.txt" ), context ); + assertActivation( true, newMissingProfile( "${basedir}/someFile.txt" ), context ); + } + + @Test + public void testIsActiveExistsFileExists() + { + assertActivation( true, newExistsProfile( "file.txt" ), context ); + assertActivation( true, newExistsProfile( "${basedir}" ), context ); + assertActivation( true, newExistsProfile( "${basedir}/" + "file.txt" ), context ); + + assertActivation( false, newMissingProfile( "file.txt" ), context ); + assertActivation( false, newMissingProfile( "${basedir}" ), context ); + assertActivation( false, newMissingProfile( "${basedir}/" + "file.txt" ), context ); + } + + @Test + public void testIsActiveExistsLeavesFileUnchanged() + { + Profile profile = newExistsProfile( "file.txt" ); + assertEquals( "file.txt", profile.getActivation().getFile().getExists() ); + + assertActivation( true, profile, context ); + + assertEquals( "file.txt", profile.getActivation().getFile().getExists() ); + } + + private Profile newExistsProfile( String filePath ) + { + ActivationFile activationFile = new ActivationFile(); + activationFile.setExists( filePath ); + return newProfile( activationFile ); + } + + private Profile newMissingProfile( String filePath ) + { + ActivationFile activationFile = new ActivationFile(); + activationFile.setMissing( filePath ); + return newProfile( activationFile ); + } + + private Profile newProfile( ActivationFile activationFile ) + { + Activation activation = new Activation(); + activation.setFile( activationFile ); + + Profile profile = new Profile(); + profile.setActivation( activation ); + + return profile; + } +}