Java tutorial
/* * Copyright (C) 2012 Red Hat, Inc. (jcasey@redhat.com) * * Licensed 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.commonjava.maven.ext.common.model; import org.apache.maven.model.Activation; import org.apache.maven.model.ActivationProperty; import org.apache.maven.model.Dependency; import org.apache.maven.model.DependencyManagement; import org.apache.maven.model.Model; import org.apache.maven.model.Parent; import org.apache.maven.model.Plugin; import org.apache.maven.model.PluginManagement; import org.apache.maven.model.Profile; import org.commonjava.maven.atlas.ident.ref.ArtifactRef; import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef; import org.commonjava.maven.atlas.ident.ref.SimpleArtifactRef; import org.commonjava.maven.atlas.ident.ref.SimpleProjectVersionRef; import org.commonjava.maven.atlas.ident.util.VersionUtils; import org.commonjava.maven.ext.common.ManipulationException; import org.commonjava.maven.ext.common.session.MavenSessionHandler; import org.commonjava.maven.ext.common.util.ProfileUtils; import org.commonjava.maven.ext.common.util.PropertyResolver; import org.commonjava.maven.galley.maven.internal.defaults.StandardMaven350PluginDefaults; import org.commonjava.maven.galley.maven.spi.defaults.MavenPluginDefaults; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import static org.apache.commons.lang.StringUtils.isEmpty; import static org.apache.commons.lang.StringUtils.isNotEmpty; /** * Provides a convenient way of passing around related information about a Maven * project without passing multiple parameters. The model in this class * represents the model that is being modified by the extension. Also stored is * the original POM file related to these models. * * @author jdcasey */ public class Project { private static final MavenPluginDefaults PLUGIN_DEFAULTS = new StandardMaven350PluginDefaults(); private final Logger logger = LoggerFactory.getLogger(getClass()); /** * Original POM file from which this model information was loaded. */ private final File pom; /** * Model undergoing modification during execution. This model is what * will eventually be written back to disk. */ private final Model model; /** * Denotes if this Project represents the top level POM of a build. */ private boolean inheritanceRoot; /** * Denotes if this Project is the execution root. */ private boolean executionRoot; private boolean incrementalPME; /** * Tracking inheritance across the project. */ private Project projectParent; public Project(final File pom, final Model model) throws ManipulationException { this.pom = pom; this.model = model; // Validate the model. if (model == null) { throw new ManipulationException("Invalid null model."); } else if (model.getVersion() == null && model.getParent() == null) { throw new ManipulationException("Invalid model: " + model + " Cannot find version!"); } } /** * Create a project with only a Model. Only used by tests currently. * @param model the Model to use. * @throws ManipulationException if an error occurs. */ public Project(final Model model) throws ManipulationException { this(model.getPomFile(), model); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + getArtifactId().hashCode(); result = prime * result + getGroupId().hashCode(); result = prime * result + getVersion().hashCode(); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Project other = (Project) obj; // Simply inlined ProjectVersionRef comparison here as ProjectVersionRef are created now // on demand to ensure they have the current values. However we are using VersionSpec.equals // in order to maintain the same semantics as ProjectVersionRef.equals. return getGroupId().equals(other.getGroupId()) && getArtifactId().equals(other.getArtifactId()) && VersionUtils.createFromSpec(getVersion()) .equals(VersionUtils.createFromSpec(other.getVersion())); } @Override public String toString() { return getKey() + " [pom=" + pom + "]"; } public File getPom() { return pom; } /** * Retrieve the model undergoing modification. * @return the Model being modified. */ public Model getModel() { return model; } public ProjectVersionRef getKey() { return new SimpleProjectVersionRef(getGroupId(), getArtifactId(), getVersion()); } public Parent getModelParent() { return model.getParent(); } /** * Returns the Project groupId. Also used by Interpolator. * @return the groupId */ public String getGroupId() { String g = model.getGroupId(); if (g == null) { // Note: reliant upon model validation that the parent is not null. g = model.getParent().getGroupId(); } return g; } /** * Returns the Project artifactId. Also used by Interpolator. * @return the artifactId */ public String getArtifactId() { return getModel().getArtifactId(); } /** * Returns the Project version. Also used by Interpolator. * @return the version */ public String getVersion() { String v = model.getVersion(); if (v == null) { // Note: reliant upon model validation that the parent is not null. v = model.getParent().getVersion(); } return v; } /** * This method will scan the dependencies in the potentially active Profiles in this project and * return a fully resolved list. Note that this will only return full dependencies not managed * i.e. those with a group, artifact and version. * * Note that while updating the {@link Dependency} reference returned will be reflected in the * Model as it is the same object, if you wish to remove or add items to the Model then you * must use {@link #getModel()} * * @param session MavenSessionHandler, used by {@link PropertyResolver} * @return a list of fully resolved {@link ArtifactRef} to the original {@link Dependency} * @throws ManipulationException if an error occurs */ public HashMap<Profile, HashMap<ArtifactRef, Dependency>> getResolvedProfileDependencies( MavenSessionHandler session) throws ManipulationException { HashMap<Profile, HashMap<ArtifactRef, Dependency>> resolvedProfileDependencies = new HashMap<>(); for (final Profile profile : ProfileUtils.getProfiles(session, model)) { HashMap<ArtifactRef, Dependency> profileDeps = new HashMap<>(); resolveDeps(session, profile.getDependencies(), false, profileDeps); resolvedProfileDependencies.put(profile, profileDeps); } return resolvedProfileDependencies; } /** * This method will scan the dependencies in the potentially active Profiles in this project and * return a fully resolved list. Note that this will return all dependencies including managed * i.e. those with a group, artifact and potentially empty version. * * Note that while updating the {@link Dependency} reference returned will be reflected in the * Model as it is the same object, if you wish to remove or add items to the Model then you * must use {@link #getModel()} * * @param session MavenSessionHandler, used by {@link PropertyResolver} * @return a list of fully resolved {@link ArtifactRef} to the original {@link Dependency} * @throws ManipulationException if an error occurs */ public HashMap<Profile, HashMap<ArtifactRef, Dependency>> getAllResolvedProfileDependencies( MavenSessionHandler session) throws ManipulationException { HashMap<Profile, HashMap<ArtifactRef, Dependency>> allResolvedProfileDependencies = new HashMap<>(); for (final Profile profile : ProfileUtils.getProfiles(session, model)) { HashMap<ArtifactRef, Dependency> profileDeps = new HashMap<>(); resolveDeps(session, profile.getDependencies(), true, profileDeps); allResolvedProfileDependencies.put(profile, profileDeps); } return allResolvedProfileDependencies; } /** * This method will scan the dependencies in the dependencyManagement section of the potentially active Profiles in * this project and return a fully resolved list. Note that while updating the {@link Dependency} * reference returned will be reflected in the Model as it is the same object, if you wish to remove or add items * to the Model then you must use {@link #getModel()} * * @param session MavenSessionHandler, used by {@link PropertyResolver} * @return a list of fully resolved {@link ArtifactRef} to the original {@link Dependency} (that were within DependencyManagement) * @throws ManipulationException if an error occurs */ public HashMap<Profile, HashMap<ArtifactRef, Dependency>> getResolvedProfileManagedDependencies( MavenSessionHandler session) throws ManipulationException { HashMap<Profile, HashMap<ArtifactRef, Dependency>> resolvedProfileManagedDependencies = new HashMap<>(); for (final Profile profile : ProfileUtils.getProfiles(session, model)) { HashMap<ArtifactRef, Dependency> profileDeps = new HashMap<>(); final DependencyManagement dm = profile.getDependencyManagement(); if (!(dm == null || dm.getDependencies() == null)) { resolveDeps(session, dm.getDependencies(), false, profileDeps); resolvedProfileManagedDependencies.put(profile, profileDeps); } } return resolvedProfileManagedDependencies; } /** * This method will scan the plugins in this project and return a fully resolved list. Note that * while updating the {@link Plugin} reference returned will be reflected in the Model as it is the * same object, if you wish to remove or add items to the Model then you must use {@link #getModel()} * * @param session MavenSessionHandler, used by {@link PropertyResolver} * @return a list of fully resolved {@link ProjectVersionRef} to the original {@link Plugin} * @throws ManipulationException if an error occurs */ public HashMap<ProjectVersionRef, Plugin> getResolvedPlugins(MavenSessionHandler session) throws ManipulationException { HashMap<ProjectVersionRef, Plugin> resolvedPlugins = new HashMap<>(); if (getModel().getBuild() != null) { resolvePlugins(session, getModel().getBuild().getPlugins(), resolvedPlugins); } return resolvedPlugins; } /** * This method will scan the plugins in the pluginManagement section of this project and return a fully * resolved list. Note that while updating the {@link Plugin} reference returned will be reflected in * the Model as it is the same object, if you wish to remove or add items to the Model then you must * use {@link #getModel()} * * @param session MavenSessionHandler, used by {@link PropertyResolver} * @return a list of fully resolved {@link ProjectVersionRef} to the original {@link Plugin} * @throws ManipulationException if an error occurs */ public HashMap<ProjectVersionRef, Plugin> getResolvedManagedPlugins(MavenSessionHandler session) throws ManipulationException { HashMap<ProjectVersionRef, Plugin> resolvedManagedPlugins = new HashMap<>(); if (getModel().getBuild() != null) { final PluginManagement pm = getModel().getBuild().getPluginManagement(); if (!(pm == null || pm.getPlugins() == null)) { resolvePlugins(session, pm.getPlugins(), resolvedManagedPlugins); } } return resolvedManagedPlugins; } /** * This method will scan the plugins in the potentially active Profiles in this project and * return a fully resolved list. Note that while updating the {@link Plugin} reference * returned will be reflected in the Model as it is the same object, if you wish to * remove or add items to the Model then you must use {@link #getModel()} * * @param session MavenSessionHandler, used by {@link PropertyResolver} * @return a list of fully resolved {@link ProjectVersionRef} to the original {@link Plugin} * @throws ManipulationException if an error occurs */ public HashMap<Profile, HashMap<ProjectVersionRef, Plugin>> getResolvedProfilePlugins( MavenSessionHandler session) throws ManipulationException { HashMap<Profile, HashMap<ProjectVersionRef, Plugin>> resolvedProfilePlugins = new HashMap<>(); for (final Profile profile : ProfileUtils.getProfiles(session, model)) { HashMap<ProjectVersionRef, Plugin> profileDeps = new HashMap<>(); if (profile.getBuild() != null) { resolvePlugins(session, profile.getBuild().getPlugins(), profileDeps); } resolvedProfilePlugins.put(profile, profileDeps); } return resolvedProfilePlugins; } /** * This method will scan the plugins in the pluginManagement section in the potentially active Profiles * in this project and return a fully resolved list. Note that while updating the {@link Plugin} * reference returned will be reflected in the Model as it is the same object, if you wish to remove * or add items to the Model then you must use {@link #getModel()} * * @param session MavenSessionHandler, used by {@link PropertyResolver} * @return a list of fully resolved {@link ProjectVersionRef} to the original {@link Plugin} * @throws ManipulationException if an error occurs */ public HashMap<Profile, HashMap<ProjectVersionRef, Plugin>> getResolvedProfileManagedPlugins( MavenSessionHandler session) throws ManipulationException { HashMap<Profile, HashMap<ProjectVersionRef, Plugin>> resolvedProfileManagedPlugins = new HashMap<>(); for (final Profile profile : ProfileUtils.getProfiles(session, model)) { HashMap<ProjectVersionRef, Plugin> profileDeps = new HashMap<>(); if (profile.getBuild() != null) { final PluginManagement pm = profile.getBuild().getPluginManagement(); if (!(pm == null || pm.getPlugins() == null)) { resolvePlugins(session, pm.getPlugins(), profileDeps); } } resolvedProfileManagedPlugins.put(profile, profileDeps); } return resolvedProfileManagedPlugins; } /** * This method will scan the dependencies in this project and return a fully resolved list. Note that this * will only return full dependencies not managed i.e. those with a group, artifact and version. * * Note that while updating the {@link Dependency} reference returned will be reflected in the Model * as it is the same object, if you wish to remove or add items to the Model then you must use {@link #getModel()} * * @param session MavenSessionHandler, used by {@link PropertyResolver} * @return a list of fully resolved {@link ArtifactRef} to the original {@link Dependency} * @throws ManipulationException if an error occurs */ public HashMap<ArtifactRef, Dependency> getResolvedDependencies(MavenSessionHandler session) throws ManipulationException { HashMap<ArtifactRef, Dependency> resolvedDependencies = new HashMap<>(); resolveDeps(session, getModel().getDependencies(), false, resolvedDependencies); return resolvedDependencies; } /** * This method will scan the dependencies in this project and return a fully resolved list. Note that this * will return all dependencies including managed i.e. those with a group, artifact and potentially empty * version. * * Note that while updating the {@link Dependency} reference returned will be reflected in the Model * as it is the same object, if you wish to remove or add items to the Model then you must use {@link #getModel()} * * @param session MavenSessionHandler, used by {@link PropertyResolver} * @return a list of fully resolved {@link ArtifactRef} to the original {@link Dependency} * @throws ManipulationException if an error occurs */ public HashMap<ArtifactRef, Dependency> getAllResolvedDependencies(MavenSessionHandler session) throws ManipulationException { HashMap<ArtifactRef, Dependency> allResolvedDependencies = new HashMap<>(); resolveDeps(session, getModel().getDependencies(), true, allResolvedDependencies); return allResolvedDependencies; } /** * This method will scan the dependencies in the dependencyManagement section of this project and return a * fully resolved list. Note that while updating the {@link Dependency} reference returned will be reflected * in the Model as it is the same object, if you wish to remove or add items to the Model then you must use {@link #getModel()} * * @param session MavenSessionHandler, used by {@link PropertyResolver} * @return a list of fully resolved {@link ArtifactRef} to the original {@link Dependency} (that were within DependencyManagement) * @throws ManipulationException if an error occurs */ public HashMap<ArtifactRef, Dependency> getResolvedManagedDependencies(MavenSessionHandler session) throws ManipulationException { HashMap<ArtifactRef, Dependency> resolvedManagedDependencies = new HashMap<>(); final DependencyManagement dm = getModel().getDependencyManagement(); if (!(dm == null || dm.getDependencies() == null)) { resolveDeps(session, dm.getDependencies(), false, resolvedManagedDependencies); } return resolvedManagedDependencies; } private void resolveDeps(MavenSessionHandler session, List<Dependency> deps, boolean includeManagedDependencies, HashMap<ArtifactRef, Dependency> resolvedDependencies) throws ManipulationException { ListIterator<Dependency> iterator = deps.listIterator(deps.size()); // Iterate in reverse order so later deps take precedence while (iterator.hasPrevious()) { Dependency d = iterator.previous(); String g = PropertyResolver.resolveInheritedProperties(session, this, "${project.groupId}".equals(d.getGroupId()) ? getGroupId() : d.getGroupId()); String a = PropertyResolver.resolveInheritedProperties(session, this, "${project.artifactId}".equals(d.getArtifactId()) ? getArtifactId() : d.getArtifactId()); String v = PropertyResolver.resolveInheritedProperties(session, this, d.getVersion()); if (includeManagedDependencies && isEmpty(v)) { v = "*"; } if (isNotEmpty(g) && isNotEmpty(a) && isNotEmpty(v)) { SimpleArtifactRef sar = new SimpleArtifactRef(g, a, v, d.getType(), d.getClassifier()); // If the GAVTC already exists within the map it means we have a duplicate entry. While Maven // technically allows this it does warn that this leads to unstable models. In PME case this breaks // the indexing as we don't have duplicate entries. Given they are exact matches, remove older duplicate. if (resolvedDependencies.containsKey(sar)) { logger.error("Found duplicate entry within dependency list. Key of {} and dependency {}", sar, d); iterator.remove(); } else { Dependency old = resolvedDependencies.put(sar, d); if (old != null) { logger.error( "Internal project dependency resolution failure ; replaced {} in store by {}:{}:{}.", old, g, a, v); throw new ManipulationException( "Internal project dependency resolution failure ; replaced " + old + " by " + d); } } } } } private void resolvePlugins(MavenSessionHandler session, List<Plugin> plugins, HashMap<ProjectVersionRef, Plugin> resolvedPlugins) throws ManipulationException { ListIterator<Plugin> iterator = plugins.listIterator(plugins.size()); // Iterate in reverse order so later plugins take precedence while (iterator.hasPrevious()) { Plugin p = iterator.previous(); String g = PropertyResolver.resolveInheritedProperties(session, this, "${project.groupId}".equals(p.getGroupId()) ? getGroupId() : p.getGroupId()); String a = PropertyResolver.resolveInheritedProperties(session, this, "${project.artifactId}".equals(p.getArtifactId()) ? getArtifactId() : p.getArtifactId()); String v = PropertyResolver.resolveInheritedProperties(session, this, p.getVersion()); // Its possible the internal plugin list is either abbreviated or empty. Attempt to fill in default values for // comparison purposes. if (isEmpty(g)) { g = PLUGIN_DEFAULTS.getDefaultGroupId(a); } // Theoretically we could default an empty v via PLUGIN_DEFAULTS.getDefaultVersion( g, a ) but // this means managed plugins would be included which confuses things. if (isNotEmpty(g) && isNotEmpty(a) && isNotEmpty(v)) { SimpleProjectVersionRef spv = new SimpleProjectVersionRef(g, a, v); // If the GAV already exists within the map it means we have a duplicate entry. While Maven // technically allows this it does warn that this leads to unstable models. In PME case this breaks // the indexing as we don't have duplicate entries. Given they are exact matches, remove older duplicate. if (resolvedPlugins.containsKey(spv)) { logger.error("Found duplicate entry within plugin list. Key of {} and plugin {}", spv, p); iterator.remove(); } else { Plugin old = resolvedPlugins.put(spv, p); if (old != null) { logger.error("Internal project plugin resolution failure ; replaced {} in store by {}.", old, spv); throw new ManipulationException( "Internal project plugin resolution failure ; replaced " + old + " by " + spv); } } } } } public void setInheritanceRoot(final boolean inheritanceRoot) { this.inheritanceRoot = inheritanceRoot; } /** * @return true if this Project represents the top level POM of a build. */ public boolean isInheritanceRoot() { return inheritanceRoot; } public void setExecutionRoot() { executionRoot = true; } /** * Returns whether this project is the execution root. * @return true if this Project is the execution root. */ public boolean isExecutionRoot() { return executionRoot; } public void setIncrementalPME(boolean incrementalPME) { this.incrementalPME = incrementalPME; } public boolean isIncrementalPME() { return incrementalPME; } public void setProjectParent(Project parent) { this.projectParent = parent; } public Project getProjectParent() { return projectParent; } /** * @return inherited projects. Returned with order of root project first, down to this project. */ public List<Project> getInheritedList() { final List<Project> found = new ArrayList<>(); found.add(this); Project loop = this; while (loop.getProjectParent() != null) { // Place inherited first so latter down tree take precedence. found.add(0, loop.getProjectParent()); loop = loop.getProjectParent(); } return found; } /** * @return inherited projects. Returned with order of this project first, up to root project. */ public List<Project> getReverseInheritedList() { final List<Project> found = new ArrayList<>(); found.add(this); Project loop = this; while (loop.getProjectParent() != null) { // Place inherited last for iteration purposes found.add(loop.getProjectParent()); loop = loop.getProjectParent(); } return found; } public void updateProfiles(List<Profile> remoteProfiles) { final List<Profile> profiles = model.getProfiles(); if (!remoteProfiles.isEmpty()) { for (Profile profile : remoteProfiles) { final Iterator<Profile> i = profiles.iterator(); while (i.hasNext()) { final Profile p = i.next(); if (profile.getId().equals(p.getId())) { logger.debug("Removing local profile {} ", p); i.remove(); // Don't break out of the loop so we can check for active profiles } // If we have injected profiles and one of the current profiles is using // activeByDefault it will get mistakenly deactivated due to the semantics // of activeByDefault. Therefore replace the activation. if (p.getActivation() != null && p.getActivation().isActiveByDefault()) { logger.warn("Profile {} is activeByDefault", p); final Activation replacement = new Activation(); final ActivationProperty replacementProp = new ActivationProperty(); replacementProp.setName("!disableProfileActivation"); replacement.setProperty(replacementProp); p.setActivation(replacement); } } logger.debug("Adding profile {}", profile); profiles.add(profile); } } } }