package org.jboss.shrinkwrap.resolver.impl.maven.aether;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathFactoryConfigurationException;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.repository.WorkspaceRepository;
import org.jboss.shrinkwrap.resolver.impl.maven.util.Validate;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public class ClasspathWorkspaceReader implements WorkspaceReader {
private static final Logger log = Logger.getLogger(ClasspathWorkspaceReader.class.getName());
private static final String CLASS_PATH_KEY = "java.class.path";
private static final String SUREFIRE_CLASS_PATH_KEY = "surefire.test.class.path";
private static final class FileInfo {
private final File file;
private final boolean isFile;
private final boolean isDirectory;
private FileInfo(final File file, final boolean isFile, final boolean isDirectory) {
this.file = file;
this.isFile = isFile;
this.isDirectory = isDirectory;
}
private FileInfo(final File file) {
this(file, file.isFile(), file.isDirectory());
}
private FileInfo(final String classpathEntry) {
this(new File(classpathEntry));
}
private File getFile() {
return file;
}
private boolean isFile() {
return isFile;
}
private boolean isDirectory() {
return isDirectory;
}
}
private final Set<String> classPathEntries = new LinkedHashSet<String>();
private final Map<String, FileInfo> classpathFileInfoCache = new HashMap<String, FileInfo>();
private final Map<File, FileInfo> pomFileInfoCache = new HashMap<File, FileInfo>();
private final Map<File, Artifact> foundArtifactCache = new HashMap<File, Artifact>();
private DocumentBuilder documentBuilder;
private XPath xPath;
private XPathExpression xPathParentGroupIdExpression;
private XPathExpression xPathGroupIdExpression;
private XPathExpression xPathArtifactIdExpression;
private XPathExpression xPathTypeExpression;
private XPathExpression xPathVersionExpression;
private XPathExpression xPathParentVersionExpression;
public ClasspathWorkspaceReader() {
final String classPath = SecurityActions.getProperty(CLASS_PATH_KEY);
final String surefireClassPath = SecurityActions.getProperty(SUREFIRE_CLASS_PATH_KEY);
this.classPathEntries.addAll(getClassPathEntries(surefireClassPath));
this.classPathEntries.addAll(getClassPathEntries(classPath));
}
@Override
public WorkspaceRepository getRepository() {
return new WorkspaceRepository("classpath");
}
@Override
public File findArtifact(final Artifact artifact) {
for (String classpathEntry : classPathEntries) {
final FileInfo fileInfo = getClasspathFileInfo(classpathEntry);
final File file = fileInfo.getFile();
if (fileInfo.isDirectory()) {
final FileInfo pomFileInfo = getPomFileInfo(file);
if (pomFileInfo != null && pomFileInfo.isFile()) {
final File pomFile = pomFileInfo.getFile();
final Artifact foundArtifact = getFoundArtifact(pomFile);
if (areEquivalent(artifact, foundArtifact)) {
return pomFile;
}
}
}
else if (fileInfo.isFile()) {
final StringBuilder name = new StringBuilder(artifact.getArtifactId()).append("-").append(
artifact.getVersion());
if (!Validate.isNullOrEmpty(artifact.getClassifier())) {
name.append("-").append(artifact.getClassifier());
}
String candidateName = file.getName();
int suffixPosition = candidateName.lastIndexOf('.');
if (suffixPosition != -1) {
candidateName = candidateName.substring(0, suffixPosition);
}
if (candidateName.equals(name.toString())) {
if ("pom".equals(artifact.getExtension())) {
final FileInfo pomFileInfo = getPomFileInfo(file);
if (pomFileInfo != null && pomFileInfo.isFile()) {
final File pomFile = pomFileInfo.getFile();
final Artifact foundArtifact = getFoundArtifact(pomFile);
if (areEquivalent(artifact, foundArtifact)) {
return pomFile;
}
}
}
name.append(".").append(artifact.getExtension());
if (file.getName().endsWith(name.toString())) {
return file;
}
}
}
}
return null;
}
private boolean areEquivalent(final Artifact artifact, final Artifact foundArtifact) {
boolean areEquivalent = (foundArtifact.getGroupId().equals(artifact.getGroupId())
&& foundArtifact.getArtifactId().equals(artifact.getArtifactId()) && foundArtifact.getVersion().equals(
artifact.getVersion()));
return areEquivalent;
}
@Override
public List<String> findVersions(final Artifact artifact) {
return Collections.emptyList();
}
private Set<String> getClassPathEntries(final String classPath) {
if (Validate.isNullOrEmpty(classPath)) {
return Collections.emptySet();
}
return new LinkedHashSet<String>(Arrays.asList(classPath.split(String.valueOf(File.pathSeparatorChar))));
}
private FileInfo getClasspathFileInfo(final String classpathEntry) {
FileInfo classpathFileInfo = classpathFileInfoCache.get(classpathEntry);
if (classpathFileInfo == null) {
classpathFileInfo = new FileInfo(classpathEntry);
classpathFileInfoCache.put(classpathEntry, classpathFileInfo);
}
return classpathFileInfo;
}
private FileInfo getPomFileInfo(final File childFile) {
FileInfo pomFileInfo = pomFileInfoCache.get(childFile);
if (pomFileInfo == null) {
pomFileInfo = createPomFileInfo(childFile);
if (pomFileInfo != null) {
pomFileInfoCache.put(childFile, pomFileInfo);
}
}
return pomFileInfo;
}
private FileInfo createPomFileInfo(final File childFile) {
File parent = childFile.getParentFile();
if (parent != null) {
parent = parent.getParentFile();
if (parent != null) {
final File pomFile = new File(parent, "pom.xml");
return new FileInfo(pomFile);
}
}
return null;
}
private Artifact getFoundArtifact(final File pomFile) {
Artifact foundArtifact = foundArtifactCache.get(pomFile);
if (foundArtifact == null) {
foundArtifact = createFoundArtifact(pomFile);
foundArtifactCache.put(pomFile, foundArtifact);
}
return foundArtifact;
}
private Artifact createFoundArtifact(final File pomFile) {
try {
if (log.isLoggable(Level.FINE)) {
log.fine("Processing " + pomFile.getAbsolutePath() + " for classpath artifact resolution");
}
final Document pom = loadPom(pomFile);
String groupId = getXPathGroupIdExpression().evaluate(pom);
String artifactId = getXPathArtifactIdExpression().evaluate(pom);
String type = getXPathTypeExpression().evaluate(pom);
String version = getXPathVersionExpression().evaluate(pom);
if (Validate.isNullOrEmpty(groupId)) {
groupId = getXPathParentGroupIdExpression().evaluate(pom);
}
if (Validate.isNullOrEmpty(type)) {
type = "jar";
}
if (version == null || version.equals("")) {
version = getXPathParentVersionExpression().evaluate(pom);
}
final Artifact foundArtifact = new DefaultArtifact(groupId + ":" + artifactId + ":" + type + ":" + version);
foundArtifact.setFile(pomFile);
return foundArtifact;
} catch (final Exception e) {
throw new RuntimeException("Could not parse pom.xml: " + pomFile, e);
}
}
private Document loadPom(final File pom) throws IOException, SAXException, ParserConfigurationException {
final DocumentBuilder documentBuilder = getDocumentBuilder();
return documentBuilder.parse(pom);
}
private DocumentBuilder getDocumentBuilder() throws ParserConfigurationException {
if (documentBuilder == null) {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
documentBuilder = factory.newDocumentBuilder();
}
return documentBuilder;
}
private XPath getXPath() {
if (xPath == null) {
XPathFactory factory;
try {
factory = XPathFactory.newInstance(XPathFactory.DEFAULT_OBJECT_MODEL_URI,
"com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl",
ClassLoader.getSystemClassLoader());
} catch (XPathFactoryConfigurationException e) {
factory = XPathFactory.newInstance();
}
xPath = factory.newXPath();
}
return xPath;
}
private XPathExpression getXPathParentGroupIdExpression() throws XPathExpressionException {
if (xPathParentGroupIdExpression == null) {
xPathParentGroupIdExpression = getXPath().compile("/project/parent/groupId");
}
return xPathParentGroupIdExpression;
}
private XPathExpression getXPathGroupIdExpression() throws XPathExpressionException {
if (xPathGroupIdExpression == null) {
xPathGroupIdExpression = getXPath().compile("/project/groupId");
}
return xPathGroupIdExpression;
}
private XPathExpression getXPathArtifactIdExpression() throws XPathExpressionException {
if (xPathArtifactIdExpression == null) {
xPathArtifactIdExpression = getXPath().compile("/project/artifactId");
}
return xPathArtifactIdExpression;
}
private XPathExpression getXPathTypeExpression() throws XPathExpressionException {
if (xPathTypeExpression == null) {
xPathTypeExpression = getXPath().compile("/project/packaging");
}
return xPathTypeExpression;
}
private XPathExpression getXPathVersionExpression() throws XPathExpressionException {
if (xPathVersionExpression == null) {
xPathVersionExpression = getXPath().compile("/project/version");
}
return xPathVersionExpression;
}
private XPathExpression getXPathParentVersionExpression() throws XPathExpressionException {
if (xPathParentVersionExpression == null) {
xPathParentVersionExpression = getXPath().compile("/project/parent/version");
}
return xPathParentVersionExpression;
}
}