/* * Copyright (c) 2011 Red Hat, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see * <http://www.gnu.org/licenses>. */ package com.redhat.rcm.version.mgr.mod; import java.util.ArrayList; 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.mae.project.key.FullProjectKey; import org.apache.maven.mae.project.key.VersionlessProjectKey; import org.apache.maven.model.Build; import org.apache.maven.model.BuildBase; import org.apache.maven.model.Model; import org.apache.maven.model.ModelBase; import org.apache.maven.model.Plugin; import org.apache.maven.model.PluginManagement; import org.apache.maven.model.Profile; import org.apache.maven.model.ReportPlugin; import org.apache.maven.model.Reporting; import org.apache.maven.model.merge.MavenModelMerger; import org.codehaus.plexus.component.annotations.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.redhat.rcm.version.mgr.session.VersionManagerSession; import com.redhat.rcm.version.model.Project; @Component( role = ProjectModder.class, hint = "toolchain-realignment" ) public class ToolchainModder implements ProjectModder { private final Logger logger = LoggerFactory.getLogger( getClass() ); private final InjectionMerger merger = new InjectionMerger(); @Override public String getDescription() { return "Forcibly realign POM build section to use plugins declared in the supplied toolchain POM (if present)."; } @Override public boolean inject( final Project project, final VersionManagerSession session ) { boolean changed = false; if ( session.getToolchainKey() == null ) { return changed; } final Set<VersionlessProjectKey> pluginRefs = new HashSet<VersionlessProjectKey>(); pluginRefs.addAll( session.getChildPluginReferences( new VersionlessProjectKey( project.getKey() ) ) ); final List<ModelBase> bases = new ArrayList<ModelBase>(); bases.add( project.getModel() ); final List<Profile> profiles = project.getModel() .getProfiles(); if ( profiles != null && !profiles.isEmpty() ) { bases.addAll( profiles ); } changed = stripRemovedPlugins( project, bases, session ) || changed; changed = stripToolchainPluginInfo( project, bases, pluginRefs, session ) || changed; if ( project.getParent() == null ) { changed = injectPluginUsages( project, pluginRefs, session ) || changed; changed = injectPluginManagement( project, pluginRefs, session ) || changed; } // NOTE: Having versions in pluginManagement isn't normally enough for // report plugins, unless they're also during the normal build process. // // So, we have to inject the versions directly into the reporting section. // // This happens regardless of whether the toolchain is in the ancestry of the POM or not. changed = adjustReportPlugins( project, bases, pluginRefs, session ) || changed; return changed; } private boolean adjustReportPlugins( final Project project, final List<ModelBase> bases, final Set<VersionlessProjectKey> pluginRefs, final VersionManagerSession session ) { final VersionlessProjectKey parentKey = project.getParent() != null ? new VersionlessProjectKey( project.getParent() ) : null; boolean changed = false; for ( final ModelBase base : bases ) { final List<ReportPlugin> reportPlugins = project.getReportPlugins( base ); if ( reportPlugins != null ) { int idx = 0; for ( ReportPlugin plugin : reportPlugins ) { VersionlessProjectKey pluginKey = new VersionlessProjectKey( plugin ); final FullProjectKey relocation = session.getRelocation( pluginKey ); if ( relocation != null ) { session.addRelocatedCoordinate( project.getPom(), pluginKey, relocation ); changed = true; final ReportPlugin plug = new ReportPlugin(); plug.setGroupId( relocation.getGroupId() ); plug.setArtifactId( relocation.getArtifactId() ); if ( session.isStrict() ) { plug.setVersion( relocation.getVersion() ); } plug.setConfiguration( plugin.getConfiguration() ); plug.setReportSets( plugin.getReportSets() ); plug.setInherited( plugin.isInherited() ); plug.setInherited( plugin.getInherited() ); plug.setLocation( "", plugin.getLocation( "" ) ); if ( plugin.getLocation( "inherited" ) != null ) { plug.setLocation( "inherited", plugin.getLocation( "inherited" ) ); } reportPlugins.set( idx, plug ); pluginKey = new VersionlessProjectKey( relocation ); plugin = plug; } final Plugin managedPlugin = session.getManagedPlugin( pluginKey ); if ( managedPlugin != null && !managedPlugin.getVersion() .equals( plugin.getVersion() ) ) { plugin.setVersion( managedPlugin.getVersion() ); changed = true; if ( parentKey != null ) { session.addChildPluginReference( parentKey, pluginKey ); } else { pluginRefs.add( pluginKey ); } } else { session.addUnmanagedPlugin( project.getPom(), plugin ); } idx++; } } } return changed; } private boolean injectPluginManagement( final Project project, final Set<VersionlessProjectKey> pluginRefs, final VersionManagerSession session ) { logger.info( "Injecting pluginManagement section from toolchain for: " + project.getKey() ); boolean changed = false; if ( pluginRefs.isEmpty() ) { return changed; } final Model original = project.getModel(); Build build = original.getBuild(); if ( build == null ) { build = new Build(); original.setBuild( build ); changed = true; } PluginManagement pm = build.getPluginManagement(); if ( pm == null ) { pm = new PluginManagement(); build.setPluginManagement( pm ); changed = true; } final Map<String, Plugin> pluginMap = pm.getPluginsAsMap(); for ( final VersionlessProjectKey pluginRef : pluginRefs ) { final Plugin managed = session.getManagedPlugin( pluginRef ); final Plugin existing = pluginMap.get( pluginRef.getId() ); if ( existing == null ) { logger.info( "Adding plugin: " + pluginRef ); pm.addPlugin( managed ); } else { logger.info( "Merging plugin: " + pluginRef ); merger.mergePlugin( managed, existing ); } changed = true; } return changed; } private boolean injectPluginUsages( final Project project, final Set<VersionlessProjectKey> pluginRefs, final VersionManagerSession session ) { logger.info( "Injecting plugin usages from toolchain for project: " + project.getKey() ); boolean changed = false; final Map<VersionlessProjectKey, Plugin> injectedPlugins = session.getInjectedPlugins(); if ( injectedPlugins.isEmpty() ) { return changed; } final Model original = project.getModel(); Build build = original.getBuild(); if ( build == null ) { build = new Build(); original.setBuild( build ); } final Map<String, Plugin> pluginMap = build.getPluginsAsMap(); for ( final Map.Entry<VersionlessProjectKey, Plugin> entry : injectedPlugins.entrySet() ) { final VersionlessProjectKey key = entry.getKey(); final Plugin injected = entry.getValue(); final Plugin existing = pluginMap.get( key.getId() ); if ( existing == null ) { logger.info( "Adding plugin: " + key ); build.addPlugin( injected ); pluginRefs.add( key ); } else { logger.info( "Merging plugin: " + key ); merger.mergePlugin( injected, existing ); } changed = true; } return changed; } private boolean stripRemovedPlugins( final Project project, final List<ModelBase> bases, final VersionManagerSession session ) { logger.info( "Deleting plugins marked for removal for project: " + project.getKey() ); boolean changed = false; final Set<VersionlessProjectKey> removedPlugins = session.getRemovedPlugins(); if ( removedPlugins.isEmpty() ) { return changed; } for ( final ModelBase base : bases ) { final BuildBase build = project.getBuild( base ); if ( build != null ) { Map<String, Plugin> pluginMap = new HashMap<String, Plugin>( build.getPluginsAsMap() ); for ( final VersionlessProjectKey key : removedPlugins ) { final Plugin existing = pluginMap.get( key.getId() ); if ( existing != null ) { logger.info( "Removing plugin: " + key ); build.removePlugin( existing ); changed = true; } } final PluginManagement pm = build.getPluginManagement(); if ( pm != null ) { pluginMap = pm.getPluginsAsMap(); for ( final VersionlessProjectKey key : removedPlugins ) { final Plugin existing = pluginMap.get( key.getId() ); if ( existing != null ) { logger.info( "Removing managed plugin: " + key ); pm.removePlugin( existing ); changed = true; } } } } final Reporting reporting = base.getReporting(); if ( reporting != null ) { final Map<String, ReportPlugin> pluginMap = new HashMap<String, ReportPlugin>( reporting.getReportPluginsAsMap() ); for ( final VersionlessProjectKey key : removedPlugins ) { final ReportPlugin existing = pluginMap.get( key.getId() ); if ( existing != null ) { logger.info( "Removing report plugin: " + key ); reporting.removePlugin( existing ); changed = true; } } } } return changed; } private boolean stripToolchainPluginInfo( final Project project, final List<ModelBase> bases, final Set<VersionlessProjectKey> pluginRefs, final VersionManagerSession session ) { logger.info( "Stripping toolchain plugin info for project: " + project.getKey() ); boolean changed = false; for ( final ModelBase base : bases ) { changed = stripToolchainPluginInfo( project, base, project.getPlugins( base ), pluginRefs, session, false ) || changed; logger.info( "Stripping toolchain pluginManagement info for project: " + project.getKey() ); changed = stripToolchainPluginInfo( project, base, project.getManagedPlugins( base ), pluginRefs, session, true ) || changed; } project.flushPluginMaps(); return changed; } private boolean stripToolchainPluginInfo( final Project project, final ModelBase base, final List<Plugin> plugins, final Set<VersionlessProjectKey> pluginRefs, final VersionManagerSession session, final boolean managed ) { boolean changed = false; if ( plugins != null ) { int idx = 0; for ( Plugin plugin : new ArrayList<Plugin>( plugins ) ) { VersionlessProjectKey pluginKey = new VersionlessProjectKey( plugin ); final FullProjectKey relocation = session.getRelocation( pluginKey ); if ( relocation != null ) { session.addRelocatedCoordinate( project.getPom(), pluginKey, relocation ); changed = true; final Plugin plug = new Plugin(); plug.setGroupId( relocation.getGroupId() ); plug.setArtifactId( relocation.getArtifactId() ); if ( session.isStrict() ) { plug.setVersion( relocation.getVersion() ); } plug.setConfiguration( plugin.getConfiguration() ); plug.setDependencies( plugin.getDependencies() ); plug.setExecutions( plugin.getExecutions() ); plug.setExtensions( plugin.isExtensions() ); plug.setExtensions( plugin.getExtensions() ); plug.setGoals( plugin.getGoals() ); plug.setInherited( plugin.isInherited() ); plug.setInherited( plugin.getInherited() ); plug.setLocation( "", plugin.getLocation( "" ) ); if ( plugin.getLocation( "inherited" ) != null ) { plug.setLocation( "inherited", plugin.getLocation( "inherited" ) ); } plugins.set( idx, plug ); pluginKey = new VersionlessProjectKey( relocation ); plugin = plug; } final Plugin managedPlugin = session.getManagedPlugin( pluginKey ); final Plugin p = plugin.clone(); if ( !session.isStrict() || managedPlugin != null ) { // Unless strict mode is set, remove the plugin version. It SHOULD come from the toolchain. // The capture-POM will assist with adding missing plugins to the toolchain. plugin.setVersion( null ); changed = true; } if ( managedPlugin != null ) { logger.info( "Stripping plugin version from: " + pluginKey ); if ( managed && isEmpty( plugin.getDependencies() ) && isEmpty( plugin.getExecutions() ) && plugin.getConfiguration() == null ) { logger.info( "Removing plugin: " + pluginKey ); plugins.remove( plugin ); changed = true; } final VersionlessProjectKey parentKey = project.getVersionlessParentKey(); if ( parentKey != null ) { session.addChildPluginReference( parentKey, pluginKey ); } else { pluginRefs.add( pluginKey ); } } else { session.addUnmanagedPlugin( project.getPom(), p ); } idx++; } } return changed; } private boolean isEmpty( final Collection<?> collection ) { return collection == null || collection.isEmpty(); } private static final class InjectionMerger extends MavenModelMerger { public void mergePlugin( final Plugin injected, final Plugin existing ) { super.mergePlugin( injected, existing, true, Collections.emptyMap() ); } } }