Java tutorial
/** * * 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. */ package com.adviser.maven; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException; import org.apache.maven.artifact.metadata.ArtifactMetadataSource; import org.apache.maven.artifact.metadata.ResolutionGroup; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.resolver.ArtifactCollector; import org.apache.maven.artifact.resolver.ArtifactResolutionException; import org.apache.maven.artifact.resolver.ArtifactResolutionResult; import org.apache.maven.artifact.resolver.CyclicDependencyException; import org.apache.maven.artifact.resolver.ResolutionListener; import org.apache.maven.artifact.resolver.ResolutionNode; import org.apache.maven.artifact.resolver.conflict.ConflictResolver; import org.apache.maven.artifact.resolver.filter.ArtifactFilter; import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.artifact.versioning.OverConstrainedVersionException; import org.apache.maven.artifact.versioning.VersionRange; public class GraphArtifactCollector implements ArtifactCollector { public ArtifactResolutionResult collect(Set<Artifact> artifacts, Artifact originatingArtifact, ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories, ArtifactMetadataSource source, ArtifactFilter filter, List<ResolutionListener> listeners) { return collect(artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository, remoteRepositories, source, filter, listeners); } public ArtifactResolutionResult collect(Set<Artifact> artifacts, Artifact originatingArtifact, Map managedVersions, ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories, ArtifactMetadataSource source, ArtifactFilter filter, List<ResolutionListener> listeners) { Map resolvedArtifacts = new HashMap(); ResolutionNode root = new ResolutionNode(originatingArtifact, remoteRepositories); try { root.addDependencies(artifacts, remoteRepositories, filter); recurse(root, resolvedArtifacts, managedVersions, localRepository, remoteRepositories, source, filter, listeners); } catch (CyclicDependencyException e) { e.printStackTrace(); } catch (OverConstrainedVersionException e) { e.printStackTrace(); } catch (ArtifactResolutionException e) { e.printStackTrace(); } Set set = new HashSet(); for (Iterator i = resolvedArtifacts.values().iterator(); i.hasNext();) { List nodes = (List) i.next(); for (Iterator j = nodes.iterator(); j.hasNext();) { ResolutionNode node = (ResolutionNode) j.next(); Artifact artifact = node.getArtifact(); try { if (!node.equals(root) && node.isActive() && node.filterTrail(filter) // If it was optional and not a direct dependency, // we don't add it or its children, just allow the // update of the version and scope && (node.isChildOfRootNode() || !artifact.isOptional())) { artifact.setDependencyTrail(node.getDependencyTrail()); set.add(node); } } catch (OverConstrainedVersionException e) { e.printStackTrace(); } } } ArtifactResolutionResult result = new ArtifactResolutionResult(); result.setArtifactResolutionNodes(set); return result; } private void recurse(ResolutionNode node, Map resolvedArtifacts, Map managedVersions, ArtifactRepository localRepository, List remoteRepositories, ArtifactMetadataSource source, ArtifactFilter filter, List listeners) throws CyclicDependencyException, ArtifactResolutionException, OverConstrainedVersionException { fireEvent(ResolutionListener.TEST_ARTIFACT, listeners, node); // TODO: use as a conflict resolver Object key = node.getKey(); if (managedVersions.containsKey(key)) { Artifact artifact = (Artifact) managedVersions.get(key); fireEvent(ResolutionListener.MANAGE_ARTIFACT, listeners, node, artifact); if (artifact.getVersion() != null) { node.getArtifact().setVersion(artifact.getVersion()); } if (artifact.getScope() != null) { node.getArtifact().setScope(artifact.getScope()); } } List previousNodes = (List) resolvedArtifacts.get(key); if (previousNodes != null) { node = checkPreviousNodes(node, listeners, previousNodes); } else { previousNodes = new ArrayList(); resolvedArtifacts.put(key, previousNodes); } previousNodes.add(node); if (node.isActive()) { fireEvent(ResolutionListener.INCLUDE_ARTIFACT, listeners, node); } // don't pull in the transitive deps of a system-scoped dependency. if (node.isActive() && !Artifact.SCOPE_SYSTEM.equals(node.getArtifact().getScope())) { fireEvent(ResolutionListener.PROCESS_CHILDREN, listeners, node); for (Iterator i = node.getChildrenIterator(); i.hasNext();) { ResolutionNode child = (ResolutionNode) i.next(); // We leave in optional ones, but don't pick up its dependencies if (!child.isResolved() && (!child.getArtifact().isOptional() || child.isChildOfRootNode())) { Artifact artifact = child.getArtifact(); try { if (artifact.getVersion() == null) { // set the recommended version // TODO: maybe its better to just pass the range // through to retrieval and use a transformation? ArtifactVersion version; version = getArtifactVersion(localRepository, remoteRepositories, source, artifact); artifact.selectVersion(version.toString()); fireEvent(ResolutionListener.SELECT_VERSION_FROM_RANGE, listeners, child); } ResolutionGroup rGroup = source.retrieve(artifact, localRepository, remoteRepositories); // TODO might be better to have source.retreive() throw // a specific exception for this situation // and catch here rather than have it return null if (rGroup == null) { // relocated dependency artifact is declared // excluded, no need to add and recurse further continue; } child.addDependencies(rGroup.getArtifacts(), rGroup.getResolutionRepositories(), filter); } catch (CyclicDependencyException e) { // would like to throw this, but we have crappy stuff in // the repo fireEvent(ResolutionListener.OMIT_FOR_CYCLE, listeners, new ResolutionNode(e.getArtifact(), remoteRepositories, child)); } catch (ArtifactMetadataRetrievalException e) { artifact.setDependencyTrail(node.getDependencyTrail()); throw new ArtifactResolutionException( "Unable to get dependency information: " + e.getMessage(), artifact, e); } recurse(child, resolvedArtifacts, managedVersions, localRepository, remoteRepositories, source, filter, listeners); } } fireEvent(ResolutionListener.FINISH_PROCESSING_CHILDREN, listeners, node); } } private ArtifactVersion getArtifactVersion(ArtifactRepository localRepository, List remoteRepositories, ArtifactMetadataSource source, Artifact artifact) throws OverConstrainedVersionException, ArtifactMetadataRetrievalException { ArtifactVersion version; if (!artifact.isSelectedVersionKnown()) { List versions = artifact.getAvailableVersions(); if (versions == null) { versions = source.retrieveAvailableVersions(artifact, localRepository, remoteRepositories); artifact.setAvailableVersions(versions); } VersionRange versionRange = artifact.getVersionRange(); version = versionRange.matchVersion(versions); if (version == null) { if (versions.isEmpty()) { throw new OverConstrainedVersionException( "No versions are present in the repository for the artifact with a range " + versionRange, artifact, remoteRepositories); } else { throw new OverConstrainedVersionException( "Couldn't find a version in " + versions + " to match range " + versionRange, artifact, remoteRepositories); } } } else { version = artifact.getSelectedVersion(); } return version; } private ResolutionNode checkPreviousNodes(ResolutionNode node, List listeners, List previousNodes) throws OverConstrainedVersionException { for (Iterator i = previousNodes.iterator(); i.hasNext();) { ResolutionNode previous = (ResolutionNode) i.next(); if (previous.isActive()) { // Version mediation VersionRange previousRange = previous.getArtifact().getVersionRange(); VersionRange currentRange = node.getArtifact().getVersionRange(); // TODO: why do we force the version on it? what if they // don't match? if (previousRange == null) { // version was already resolved node.getArtifact().setVersion(previous.getArtifact().getVersion()); } else if (currentRange == null) { // version was already resolved previous.getArtifact().setVersion(node.getArtifact().getVersion()); } else { // TODO: shouldn't need to double up on this work, only // done for simplicity of handling recommended // version but the restriction is identical VersionRange newRange = previousRange.restrict(currentRange); // TODO: ick. this forces the OCE that should have come // from the previous call. It is still correct if (newRange.isSelectedVersionKnown(previous.getArtifact())) { fireEvent(ResolutionListener.RESTRICT_RANGE, listeners, node, previous.getArtifact(), newRange); } previous.getArtifact().setVersionRange(newRange); node.getArtifact().setVersionRange(currentRange.restrict(previousRange)); // Select an appropriate available version from the (now // restricted) range // Note this version was selected before to get the // appropriate POM // But it was reset by the call to setVersionRange on // restricting the version ResolutionNode[] resetNodes = { previous, node }; for (int j = 0; j < 2; j++) { Artifact resetArtifact = resetNodes[j].getArtifact(); if (resetArtifact.getVersion() == null && resetArtifact.getVersionRange() != null && resetArtifact.getAvailableVersions() != null) { resetArtifact.selectVersion(resetArtifact.getVersionRange() .matchVersion(resetArtifact.getAvailableVersions()).toString()); fireEvent(ResolutionListener.SELECT_VERSION_FROM_RANGE, listeners, resetNodes[j]); } } } // Conflict Resolution // TODO: use as conflict resolver(s), chain // TODO: should this be part of mediation? // previous one is more dominant if (previous.getDepth() <= node.getDepth()) { checkScopeUpdate(node, previous, listeners); } else { checkScopeUpdate(previous, node, listeners); } if (previous.getDepth() <= node.getDepth()) { // previous was nearer fireEvent(ResolutionListener.OMIT_FOR_NEARER, listeners, node, previous.getArtifact()); node.disable(); node = previous; } else { fireEvent(ResolutionListener.OMIT_FOR_NEARER, listeners, previous, node.getArtifact()); previous.disable(); } } } return node; } private void checkScopeUpdate(ResolutionNode farthest, ResolutionNode nearest, List listeners) { boolean updateScope = false; Artifact farthestArtifact = farthest.getArtifact(); Artifact nearestArtifact = nearest.getArtifact(); if (Artifact.SCOPE_RUNTIME.equals(farthestArtifact.getScope()) && (Artifact.SCOPE_TEST.equals(nearestArtifact.getScope()) || Artifact.SCOPE_PROVIDED.equals(nearestArtifact.getScope()))) { updateScope = true; } if (Artifact.SCOPE_COMPILE.equals(farthestArtifact.getScope()) && !Artifact.SCOPE_COMPILE.equals(nearestArtifact.getScope())) { updateScope = true; } // current POM rules all if (nearest.getDepth() < 2 && updateScope) { updateScope = false; fireEvent(ResolutionListener.UPDATE_SCOPE_CURRENT_POM, listeners, nearest, farthestArtifact); } if (updateScope) { fireEvent(ResolutionListener.UPDATE_SCOPE, listeners, nearest, farthestArtifact); // previously we cloned the artifact, but it is more effecient to // just update the scope // if problems are later discovered that the original object needs // its original scope value, cloning may // again be appropriate nearestArtifact.setScope(farthestArtifact.getScope()); } } private void fireEvent(int event, List listeners, ResolutionNode node) { fireEvent(event, listeners, node, null); } private void fireEvent(int event, List listeners, ResolutionNode node, Artifact replacement) { fireEvent(event, listeners, node, replacement, null); } private void fireEvent(int event, List listeners, ResolutionNode node, Artifact replacement, VersionRange newRange) { for (Iterator i = listeners.iterator(); i.hasNext();) { ResolutionListener listener = (ResolutionListener) i.next(); switch (event) { case ResolutionListener.TEST_ARTIFACT: listener.testArtifact(node.getArtifact()); break; case ResolutionListener.PROCESS_CHILDREN: listener.startProcessChildren(node.getArtifact()); break; case ResolutionListener.FINISH_PROCESSING_CHILDREN: listener.endProcessChildren(node.getArtifact()); break; case ResolutionListener.INCLUDE_ARTIFACT: listener.includeArtifact(node.getArtifact()); break; case ResolutionListener.OMIT_FOR_NEARER: String version = node.getArtifact().getVersion(); String replacementVersion = replacement.getVersion(); if (version != null ? !version.equals(replacementVersion) : replacementVersion != null) { listener.omitForNearer(node.getArtifact(), replacement); } break; case ResolutionListener.OMIT_FOR_CYCLE: listener.omitForCycle(node.getArtifact()); break; case ResolutionListener.UPDATE_SCOPE: listener.updateScope(node.getArtifact(), replacement.getScope()); break; case ResolutionListener.UPDATE_SCOPE_CURRENT_POM: listener.updateScopeCurrentPom(node.getArtifact(), replacement.getScope()); break; case ResolutionListener.MANAGE_ARTIFACT: listener.manageArtifact(node.getArtifact(), replacement); break; case ResolutionListener.SELECT_VERSION_FROM_RANGE: listener.selectVersionFromRange(node.getArtifact()); break; case ResolutionListener.RESTRICT_RANGE: if (node.getArtifact().getVersionRange().hasRestrictions() || replacement.getVersionRange().hasRestrictions()) { listener.restrictRange(node.getArtifact(), replacement, newRange); } break; default: throw new IllegalStateException("Unknown event: " + event); } } } public ArtifactResolutionResult collect(Set<Artifact> artifacts, Artifact originatingArtifact, Map managedVersions, ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories, ArtifactMetadataSource source, ArtifactFilter filter, List<ResolutionListener> listeners, List<ConflictResolver> conflictResolvers) throws ArtifactResolutionException { return null; } }