| /* |
| * 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 |
| * |
| * https://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.ivy.plugins.parser.m2; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URL; |
| import java.text.ParseException; |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.ivy.core.IvyContext; |
| import org.apache.ivy.core.cache.ArtifactOrigin; |
| import org.apache.ivy.core.module.descriptor.Artifact; |
| import org.apache.ivy.core.module.descriptor.Configuration; |
| import org.apache.ivy.core.module.descriptor.DefaultArtifact; |
| import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor; |
| import org.apache.ivy.core.module.descriptor.DependencyDescriptor; |
| import org.apache.ivy.core.module.descriptor.License; |
| import org.apache.ivy.core.module.descriptor.ModuleDescriptor; |
| import org.apache.ivy.core.module.id.ModuleRevisionId; |
| import org.apache.ivy.core.resolve.ResolveData; |
| import org.apache.ivy.core.resolve.ResolveEngine; |
| import org.apache.ivy.core.resolve.ResolveOptions; |
| import org.apache.ivy.core.resolve.ResolvedModuleRevision; |
| import org.apache.ivy.plugins.circular.CircularDependencyException; |
| import org.apache.ivy.plugins.parser.ModuleDescriptorParser; |
| import org.apache.ivy.plugins.parser.ParserSettings; |
| import org.apache.ivy.plugins.parser.m2.PomModuleDescriptorBuilder.PomDependencyDescriptor; |
| import org.apache.ivy.plugins.parser.m2.PomReader.PomDependencyData; |
| import org.apache.ivy.plugins.parser.m2.PomReader.PomDependencyMgtElement; |
| import org.apache.ivy.plugins.parser.m2.PomReader.PomPluginElement; |
| import org.apache.ivy.plugins.parser.m2.PomReader.PomProfileElement; |
| import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter; |
| import org.apache.ivy.plugins.repository.Resource; |
| import org.apache.ivy.plugins.repository.url.URLResource; |
| import org.apache.ivy.plugins.resolver.DependencyResolver; |
| import org.apache.ivy.util.Message; |
| import org.xml.sax.SAXException; |
| |
| import static org.apache.ivy.core.module.descriptor.Configuration.Visibility.PUBLIC; |
| import static org.apache.ivy.plugins.namespace.NameSpaceHelper.toSystem; |
| import static org.apache.ivy.plugins.parser.m2.PomModuleDescriptorBuilder.MAVEN2_CONFIGURATIONS; |
| import static org.apache.ivy.plugins.parser.m2.PomModuleDescriptorBuilder.extractPomProperties; |
| import static org.apache.ivy.plugins.parser.m2.PomModuleDescriptorBuilder.getDependencyManagements; |
| import static org.apache.ivy.plugins.parser.m2.PomModuleDescriptorBuilder.getPlugins; |
| |
| /** |
| * A parser for Maven 2 POM. |
| * <p> |
| * The configurations used in the generated module descriptor mimics the behavior defined by Maven 2 |
| * scopes, as documented <a href= |
| * "http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html">here</a>. |
| * The PomModuleDescriptorParser use a PomDomReader to read the pom, and the |
| * PomModuleDescriptorBuilder to write the ivy module descriptor using the info read by the |
| * PomDomReader. |
| * </p> |
| */ |
| public final class PomModuleDescriptorParser implements ModuleDescriptorParser { |
| |
| private static final PomModuleDescriptorParser INSTANCE = new PomModuleDescriptorParser(); |
| |
| private static final String PARENT_MAP_KEY = PomModuleDescriptorParser.class.getName() + ".parentMap"; |
| |
| public static PomModuleDescriptorParser getInstance() { |
| return INSTANCE; |
| } |
| |
| private PomModuleDescriptorParser() { |
| } |
| |
| public void toIvyFile(InputStream is, Resource res, File destFile, ModuleDescriptor md) |
| throws ParseException, IOException { |
| try { |
| XmlModuleDescriptorWriter.write(md, destFile); |
| } finally { |
| if (is != null) { |
| is.close(); |
| } |
| } |
| } |
| |
| public boolean accept(Resource res) { |
| return res.getName().endsWith(".pom") || res.getName().endsWith("pom.xml") |
| || res.getName().endsWith("project.xml"); |
| } |
| |
| public String toString() { |
| return "pom parser"; |
| } |
| |
| public Artifact getMetadataArtifact(ModuleRevisionId mrid, Resource res) { |
| return DefaultArtifact.newPomArtifact(mrid, new Date(res.getLastModified())); |
| } |
| |
| public String getType() { |
| return "pom"; |
| } |
| |
| public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL descriptorURL, |
| boolean validate) throws ParseException, IOException { |
| URLResource resource = new URLResource(descriptorURL); |
| return parseDescriptor(ivySettings, descriptorURL, resource, validate); |
| } |
| |
| public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL descriptorURL, |
| Resource res, boolean validate) throws ParseException, IOException { |
| |
| PomModuleDescriptorBuilder mdBuilder = new PomModuleDescriptorBuilder(this, res, |
| ivySettings); |
| |
| try { |
| final IvyContext ivyContext = IvyContext.pushNewCopyContext(); |
| Set<ModuleRevisionId> parents = ivyContext.get(PARENT_MAP_KEY); |
| if (parents == null) { |
| parents = new LinkedHashSet<>(); |
| ivyContext.set(PARENT_MAP_KEY, parents); |
| } |
| |
| PomReader domReader = new PomReader(descriptorURL, res); |
| domReader.setProperty("parent.version", domReader.getParentVersion()); |
| domReader.setProperty("parent.groupId", domReader.getParentGroupId()); |
| domReader.setProperty("project.parent.version", domReader.getParentVersion()); |
| domReader.setProperty("project.parent.groupId", domReader.getParentGroupId()); |
| |
| Message.debug("parent.groupId: " + domReader.getParentGroupId()); |
| Message.debug("parent.artifactId: " + domReader.getParentArtifactId()); |
| Message.debug("parent.version: " + domReader.getParentVersion()); |
| |
| for (final Map.Entry<String, String> prop : domReader.getPomProperties().entrySet()) { |
| domReader.setProperty(prop.getKey(), prop.getValue()); |
| mdBuilder.addProperty(prop.getKey(), prop.getValue()); |
| } |
| final List<PomProfileElement> activeProfiles = new ArrayList<>(); |
| // add profile specific properties |
| for (final PomProfileElement profile : domReader.getProfiles()) { |
| if (!profile.isActive()) { |
| continue; |
| } |
| // keep track of this active profile for later use |
| activeProfiles.add(profile); |
| |
| final Map<String, String> profileProps = profile.getProfileProperties(); |
| if (profileProps.isEmpty()) { |
| continue; |
| } |
| for (final Map.Entry<String, String> entry : profileProps.entrySet()) { |
| domReader.setProperty(entry.getKey(), entry.getValue()); |
| mdBuilder.addProperty(entry.getKey(), entry.getValue()); |
| } |
| } |
| |
| ModuleDescriptor parentDescr = null; |
| if (domReader.hasParent()) { |
| // Is there any other parent properties? |
| |
| ModuleRevisionId parentModRevID = ModuleRevisionId.newInstance( |
| domReader.getParentGroupId(), domReader.getParentArtifactId(), |
| domReader.getParentVersion()); |
| |
| // check for cycles |
| if (parents.contains(parentModRevID)) { |
| throw new CircularDependencyException(parents); |
| } else { |
| parents.add(parentModRevID); |
| } |
| |
| final ResolvedModuleRevision parentModule = parseOtherPom(ivySettings, parentModRevID, true); |
| if (parentModule == null) { |
| throw new IOException("Impossible to load parent for " + res.getName() |
| + ". Parent=" + parentModRevID); |
| } |
| parentDescr = parentModule.getDescriptor(); |
| if (parentDescr != null) { |
| for (Map.Entry<String, String> prop |
| : extractPomProperties(parentDescr.getExtraInfos()).entrySet()) { |
| domReader.setProperty(prop.getKey(), prop.getValue()); |
| } |
| } |
| } |
| |
| String groupId = domReader.getGroupId(); |
| String artifactId = domReader.getArtifactId(); |
| String version = domReader.getVersion(); |
| mdBuilder.setModuleRevId(groupId, artifactId, version); |
| |
| mdBuilder.setHomePage(domReader.getHomePage()); |
| mdBuilder.setDescription(domReader.getDescription()); |
| // if this module doesn't have an explicit license, use the parent's license (if any) |
| final License[] licenses = domReader.getLicenses(); |
| if (licenses != null && licenses.length > 0) { |
| mdBuilder.setLicenses(licenses); |
| } else if (parentDescr != null) { |
| mdBuilder.setLicenses(parentDescr.getLicenses()); |
| } |
| |
| ModuleRevisionId relocation = domReader.getRelocation(); |
| |
| if (relocation != null) { |
| if (groupId != null && artifactId != null && artifactId.equals(relocation.getName()) |
| && groupId.equals(relocation.getOrganisation())) { |
| Message.error("Relocation to an other version number not supported in ivy : " |
| + mdBuilder.getModuleDescriptor().getModuleRevisionId() |
| + " relocated to " + relocation |
| + ". Please update your dependency to directly use the right version."); |
| Message.warn("Resolution will only pick dependencies of the relocated element." |
| + " Artifact and other metadata will be ignored."); |
| ResolvedModuleRevision relocatedModule = parseOtherPom(ivySettings, relocation, false); |
| if (relocatedModule == null) { |
| throw new ParseException( |
| "impossible to load module " + relocation + " to which " |
| + mdBuilder.getModuleDescriptor().getModuleRevisionId() |
| + " has been relocated", |
| 0); |
| } |
| for (DependencyDescriptor dd : relocatedModule.getDescriptor() |
| .getDependencies()) { |
| mdBuilder.addDependency(dd); |
| } |
| } else { |
| Message.info( |
| mdBuilder.getModuleDescriptor().getModuleRevisionId() + " is relocated to " |
| + relocation + ". Please update your dependencies."); |
| Message.verbose("Relocated module will be considered as a dependency"); |
| DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor( |
| mdBuilder.getModuleDescriptor(), relocation, true, false, true); |
| /* Map all public dependencies */ |
| for (Configuration m2Conf : MAVEN2_CONFIGURATIONS) { |
| if (PUBLIC.equals(m2Conf.getVisibility())) { |
| dd.addDependencyConfiguration(m2Conf.getName(), m2Conf.getName()); |
| } |
| } |
| mdBuilder.addDependency(dd); |
| } |
| } else { |
| domReader.setProperty("project.groupId", groupId); |
| domReader.setProperty("pom.groupId", groupId); |
| domReader.setProperty("groupId", groupId); |
| domReader.setProperty("project.artifactId", artifactId); |
| domReader.setProperty("pom.artifactId", artifactId); |
| domReader.setProperty("artifactId", artifactId); |
| domReader.setProperty("project.version", version); |
| domReader.setProperty("pom.version", version); |
| domReader.setProperty("version", version); |
| |
| if (parentDescr != null) { |
| mdBuilder.addExtraInfos(parentDescr.getExtraInfos()); |
| |
| // add dependency management info from parent |
| for (PomDependencyMgt dep : getDependencyManagements(parentDescr)) { |
| if (dep instanceof PomDependencyMgtElement) { |
| dep = domReader.new PomDependencyMgtElement( |
| (PomDependencyMgtElement) dep); |
| } |
| mdBuilder.addDependencyMgt(dep); |
| } |
| |
| // add plugins from parent |
| for (PomDependencyMgt pomDependencyMgt : getPlugins(parentDescr)) { |
| mdBuilder.addPlugin(pomDependencyMgt); |
| } |
| } |
| |
| for (PomDependencyMgt dep : domReader.getDependencyMgt()) { |
| addTo(mdBuilder, dep, ivySettings); |
| } |
| for (PomDependencyData dep : domReader.getDependencies()) { |
| mdBuilder.addDependency(res, dep); |
| } |
| |
| for (PomPluginElement plugin : domReader.getPlugins()) { |
| mdBuilder.addPlugin(plugin); |
| } |
| |
| // consult active profiles: |
| for (final PomProfileElement activeProfile : activeProfiles) { |
| for (PomDependencyMgt dep : activeProfile.getDependencyMgt()) { |
| addTo(mdBuilder, dep, ivySettings); |
| } |
| for (PomDependencyData dep : activeProfile.getDependencies()) { |
| mdBuilder.addDependency(res, dep); |
| } |
| for (PomPluginElement plugin : activeProfile.getPlugins()) { |
| mdBuilder.addPlugin(plugin); |
| } |
| } |
| |
| if (parentDescr != null) { |
| for (DependencyDescriptor descriptor : parentDescr.getDependencies()) { |
| if (descriptor instanceof PomDependencyDescriptor) { |
| PomDependencyData parentDep = ((PomDependencyDescriptor) descriptor) |
| .getPomDependencyData(); |
| PomDependencyData dep = domReader.new PomDependencyData(parentDep); |
| mdBuilder.addDependency(res, dep); |
| } else { |
| mdBuilder.addDependency(descriptor); |
| } |
| } |
| } |
| |
| mdBuilder.addMainArtifact(artifactId, domReader.getPackaging()); |
| |
| addSourcesAndJavadocArtifactsIfPresent(mdBuilder, ivySettings); |
| } |
| } catch (SAXException e) { |
| throw newParserException(e); |
| } finally { |
| IvyContext.popContext(); |
| } |
| |
| return mdBuilder.getModuleDescriptor(); |
| } |
| |
| private void addTo(PomModuleDescriptorBuilder mdBuilder, PomDependencyMgt dep, |
| ParserSettings ivySettings) throws ParseException, IOException { |
| if ("import".equals(dep.getScope())) { |
| // In Maven, "import" scope semantics are equivalent to getting (only) the |
| // dependency management section of the imported module, into the current |
| // module, so that those "managed dependency versions" are usable/applicable |
| // in the current module's dependencies |
| ModuleRevisionId importModRevID = ModuleRevisionId.newInstance(dep.getGroupId(), |
| dep.getArtifactId(), dep.getVersion()); |
| ResolvedModuleRevision importModule = parseOtherPom(ivySettings, importModRevID, false); |
| if (importModule == null) { |
| throw new IOException("Impossible to import module for " |
| + mdBuilder.getModuleDescriptor().getResource().getName() + ". Import=" |
| + importModRevID); |
| } |
| ModuleDescriptor importDescr = importModule.getDescriptor(); |
| |
| // add dependency management info from imported module |
| for (PomDependencyMgt importedDepMgt : getDependencyManagements(importDescr)) { |
| mdBuilder.addDependencyMgt(new DefaultPomDependencyMgt(importedDepMgt.getGroupId(), |
| importedDepMgt.getArtifactId(), importedDepMgt.getVersion(), |
| importedDepMgt.getScope(), importedDepMgt.getExcludedModules())); |
| } |
| } else { |
| mdBuilder.addDependencyMgt(dep); |
| } |
| |
| } |
| |
| private void addSourcesAndJavadocArtifactsIfPresent(PomModuleDescriptorBuilder mdBuilder, |
| ParserSettings ivySettings) { |
| if (mdBuilder.getMainArtifact() == null) { |
| // no main artifact in pom, we don't need to search for meta artifacts |
| return; |
| } |
| |
| boolean sourcesLookup = !"false" |
| .equals(ivySettings.getVariable("ivy.maven.lookup.sources")); |
| boolean javadocLookup = !"false" |
| .equals(ivySettings.getVariable("ivy.maven.lookup.javadoc")); |
| if (!sourcesLookup && !javadocLookup) { |
| Message.debug("Sources and javadocs lookup disabled"); |
| return; |
| } |
| |
| ModuleDescriptor md = mdBuilder.getModuleDescriptor(); |
| ModuleRevisionId mrid = md.getModuleRevisionId(); |
| DependencyResolver resolver = ivySettings.getResolver(mrid); |
| |
| if (resolver == null) { |
| Message.debug( |
| "no resolver found for " + mrid + ": no source or javadoc artifact lookup"); |
| } else { |
| ArtifactOrigin mainArtifact = resolver.locate(mdBuilder.getMainArtifact()); |
| |
| if (!ArtifactOrigin.isUnknown(mainArtifact)) { |
| String mainArtifactLocation = mainArtifact.getLocation(); |
| |
| if (sourcesLookup) { |
| ArtifactOrigin sourceArtifact = resolver.locate(mdBuilder.getSourceArtifact()); |
| if (!ArtifactOrigin.isUnknown(sourceArtifact) |
| && !sourceArtifact.getLocation().equals(mainArtifactLocation)) { |
| Message.debug("source artifact found for " + mrid); |
| mdBuilder.addSourceArtifact(); |
| } else { |
| // it seems that sometimes the 'src' classifier is used instead of 'sources' |
| // Cfr. IVY-1138 |
| ArtifactOrigin srcArtifact = resolver.locate(mdBuilder.getSrcArtifact()); |
| if (!ArtifactOrigin.isUnknown(srcArtifact) |
| && !srcArtifact.getLocation().equals(mainArtifactLocation)) { |
| Message.debug("source artifact found for " + mrid); |
| mdBuilder.addSrcArtifact(); |
| } else { |
| Message.debug("no source artifact found for " + mrid); |
| } |
| } |
| } else { |
| Message.debug("sources lookup disabled"); |
| } |
| |
| if (javadocLookup) { |
| ArtifactOrigin javadocArtifact = resolver |
| .locate(mdBuilder.getJavadocArtifact()); |
| if (!ArtifactOrigin.isUnknown(javadocArtifact) |
| && !javadocArtifact.getLocation().equals(mainArtifactLocation)) { |
| Message.debug("javadoc artifact found for " + mrid); |
| mdBuilder.addJavadocArtifact(); |
| } else { |
| Message.debug("no javadoc artifact found for " + mrid); |
| } |
| } else { |
| Message.debug("javadocs lookup disabled"); |
| } |
| } |
| } |
| } |
| |
| private ResolvedModuleRevision parseOtherPom(final ParserSettings ivySettings, |
| final ModuleRevisionId parentModRevID, final boolean isParentPom) throws ParseException { |
| |
| Set<ModuleRevisionId> previousParents = null; |
| if (!isParentPom) { |
| // IVY-1588: we "reset" the parent tracking, since the parent tracking should only be |
| // non-null when we are parsing a parent pom. |
| previousParents = IvyContext.getContext().get(PARENT_MAP_KEY); |
| if (previousParents != null) { |
| IvyContext.getContext().set(PARENT_MAP_KEY, null); |
| } |
| } |
| try { |
| DependencyDescriptor dd = new DefaultDependencyDescriptor(parentModRevID, true); |
| ResolveData data = IvyContext.getContext().getResolveData(); |
| if (data == null) { |
| ResolveEngine engine = IvyContext.getContext().getIvy().getResolveEngine(); |
| ResolveOptions options = new ResolveOptions(); |
| options.setDownload(false); |
| data = new ResolveData(engine, options); |
| } |
| |
| DependencyResolver resolver = ivySettings.getResolver(parentModRevID); |
| if (resolver == null) { |
| // TODO: Throw exception here? |
| return null; |
| } |
| dd = toSystem(dd, ivySettings.getContextNamespace()); |
| return resolver.getDependency(dd, data); |
| } finally { |
| if (!isParentPom) { |
| // switch back to the previous state of the parent tracking |
| IvyContext.getContext().set(PARENT_MAP_KEY, previousParents); |
| } |
| } |
| } |
| |
| private ParseException newParserException(Exception e) { |
| Message.error(e.getMessage()); |
| ParseException pe = new ParseException(e.getMessage(), 0); |
| pe.initCause(e); |
| return pe; |
| } |
| |
| } |