J Fabian Meier
J Fabian Meier

Reputation: 35843

Maven extension for adding profile

Can a Maven extension add an additional profile to a POM (during the build)?

Or are profiles already resolved when Maven extensions run?

Note that I am not talking about Maven plugins, but about Maven extensions.

Upvotes: 0

Views: 77

Answers (1)

Andrey B. Panfilov
Andrey B. Panfilov

Reputation: 6063

I do see following option there: try to modify raw xml prior building project object model, something like:

@Component(role = ModelBuilder.class, hint = Hints.DEFAULT_HINT)
public class IMHModelBuilder extends DefaultModelBuilder implements ModelBuilder {

    @Override
    public ModelBuildingResult build(ModelBuildingRequest request) throws ModelBuildingException {
        injectProfiles(request);
        return super.build(request);
    }

    @Override
    public ModelBuildingResult build(ModelBuildingRequest request, ModelBuildingResult result) throws ModelBuildingException {
        injectProfiles(request);
        return super.build(request, result);
    }

    @Override
    public Result<? extends Model> buildRawModel(File pomFile, int validationLevel, boolean locationTracking) {
        return super.buildRawModel(pomFile, validationLevel, locationTracking);
    }

    protected void injectProfiles(ModelBuildingRequest request) {
        File pomFile = request.getPomFile();
        if (pomFile == null) {
            // todo: does the absence of pom file means
            //  we encountered parent/dependency pom?
            return;
        }
        // todo: do we need to pay attention to request#getRawModel?
        // todo: do we need to pay attention to request#getModelSource?
        request.setModelSource(new ProfileAwareSource(new FileModelSource(pomFile)));
    }

    protected InputStream injectProfiles(InputStream inputStream) throws XmlPullParserException, IOException {
        MavenXpp3Reader reader = new MavenXpp3Reader();
        Model projectModel = reader.read(inputStream);
        Model profileModel = reader.read(new StringReader(buildProfile()), false);
        Profile profile = profileModel.getProfiles().get(0);

        // todo: merge with the existing ones
        projectModel.getProfiles().add(profile);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        MavenXpp3Writer writer = new MavenXpp3Writer();
        writer.write(baos, projectModel);

        return new ByteArrayInputStream(baos.toByteArray());
    }


    protected String buildProfile() {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<project>\n" +
                "    <modelVersion>4.0.0</modelVersion>\n" +
                "    <groupId>org.apache.maven</groupId>\n" +
                "    <artifactId>standalone-pom</artifactId>\n" +
                "    <version>1</version>\n" +
                "    <packaging>pom</packaging>\n" +
                "    <name>Maven Stub Project (No POM)</name>\n" +
                "    <profiles>\n" +
                "        <profile>\n" +
                "            <id>myprofile</id>\n" +
                "        </profile>\n" +
                "    </profiles>\n" +
                "</project>\n";
    }


    class ProfileAwareSource implements ModelSource2 {

        private final ModelSource2 origSource;

        public ProfileAwareSource(ModelSource2 origSource) {
            this.origSource = origSource;
        }

        @Override
        public ModelSource2 getRelatedSource(String relPath) {
            return new ProfileAwareSource(origSource.getRelatedSource(relPath));
        }

        @Override
        public URI getLocationURI() {
            return origSource.getLocationURI();
        }

        @Override
        public InputStream getInputStream() throws IOException {
            try (InputStream stream = origSource.getInputStream()) {
                return injectProfiles(stream);
            } catch (XmlPullParserException ex) {
                throw new IOException(ex);
            }
        }

        @Override
        public String getLocation() {
            return origSource.getLocation();
        }
    }

}

IMO, in case of this approach there are following challenging parts:

  • need figure out what type of model is currently being built: module, parent, aggregator or dependency
  • need somehow merge profile to be injected with the existing ones
  • I have no idea how that will work with other plugins which are "modifying" POM (flatten-maven-plugin, for example)

pros: such approach seems to be less invasive - even IntelliJ recognises new profile, although it's maven support is very poor.

Upvotes: 2

Related Questions