package jef.tools.maven; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import jef.common.log.LogUtil; import jef.tools.Assert; import jef.tools.SimpleXPath; import jef.tools.StringUtils; import jef.tools.X; import jef.tools.XMLUtils; import jef.tools.maven.jaxb.Dependency; import jef.tools.maven.jaxb.Dependency.Exclusions; import jef.tools.maven.jaxb.Exclusion; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; public class MavenDependencyParser { public static boolean debug = false; public static List<File> parseDependency(File pomFile) { File m2 = getM2(); Map<String, Dependency> set = new HashMap<String, Dependency>(); try { parseDependency(new Pom(pomFile, true), set, null, m2); } catch (SAXException e) { LogUtil.exception(e); } catch (IOException e) { LogUtil.exception(e); } List<File> result = new ArrayList<File>(set.size()); for (Dependency d : set.values()) { result.add(new File(m2, getJarFileName(d))); } return result; } private static String getKey(Dependency dep) { return dep.getGroupId() + "-" + dep.getArtifactId(); } static class ExtendsAsset { final Map<String, String> properties = new HashMap<String, String>(); final Map<String, Dependency> dependencies = new HashMap<String, Dependency>(); public void add(Dependency d) { dependencies.put(getKey(d), d); } } static class Pom { File pomFile; private Document document; boolean isNative; Pom(File file) { this.pomFile = file; } Pom(File file, boolean isNative) { this.pomFile = file; this.isNative = isNative; } public Document getDocument() throws SAXException, IOException { if (document == null) { document = XMLUtils.loadDocument(pomFile); } return document; } @Override public String toString() { return pomFile.toString(); } } private static void parseDependency(Pom pomFile, Map<String, Dependency> result, Set<String> exclusions, File m2) throws SAXException, IOException { if (exclusions == null) exclusions = new HashSet<String>(); // 首先向上追溯,获得上级中全部的变量,和继承得到的依赖(,还有父级别依赖的排除。) // 其中排除隶属于父级依赖,一旦子级依赖中有了该依赖,那么连同排除一起覆盖父级 ExtendsAsset parent = new ExtendsAsset(); parseExtends(parent, pomFile); // 向下级解析 for (Dependency d : parent.dependencies.values()) { String groupId = d.getGroupId(); String artifactId = d.getArtifactId(); String version = d.getVersion(); File jarFile = new File(m2, getJarFileName(d)); if (exclusions.contains(getKey(d))) { continue; } if (jarFile.exists()) { if (debug) { LogUtil.debug("loading " + jarFile.getAbsolutePath() + " .."); } Dependency old = result.put(getKey(d), d); if (old != null) { if (canReplace(d, old)) { result.put(getKey(d), old);// replaced with old dep, // so no need calc... if (debug) { LogUtil.debug("skip:" + d); } continue; } if (debug) { LogUtil.debug("skip:" + old); } } } else { // if(debug){ // LogUtil.warn(" NOT EXIST: " + jarFile.getAbsolutePath()); // } continue; } Set<String> newExclusion = new HashSet<String>(); newExclusion.addAll(exclusions); for (Exclusion ex : d.getExclusions().getExclusion()) { newExclusion.add(ex.getGroupId() + "-" + ex.getArtifactId()); } String childPomFileName = getChildPomName(groupId, artifactId, version); File childPomFile = new File(m2, childPomFileName); if (childPomFile.exists()) { parseDependency(new Pom(childPomFile), result, newExclusion, m2); } else { // LogUtil.warn("POM NOT EXIST: " + // childPomFile.getAbsolutePath()); } } } // 传入本级DOC,载入父级DOC,按倒序 private static List<Pom> getParents(Pom doc) throws SAXException, IOException { LinkedList<Pom> list = new LinkedList<Pom>(); list.add(doc); while (doc != null) { doc = getParentPomFile(doc); if (doc != null) list.addFirst(doc); } return list; } private static Pom getParentPomFile(Pom doc) throws SAXException, IOException { Element parent = X.$("/project/parent", doc.getDocument()); if (parent != null) { String pgroupId = X.nodeText(parent, "groupId"); String partifactId = X.nodeText(parent, "artifactId"); String pversion = X.nodeText(parent, "version"); String relativePath = X.nodeText(parent, "relativePath"); File pPomFile; // if(StringUtils.isEmpty(relativePath)){ StringBuilder sb = new StringBuilder(); sb.append(pgroupId.replace('.', '/')).append('/'); sb.append(partifactId.replace('.', '.')); sb.append("/"); sb.append(pversion).append('/').append(partifactId).append('-').append(pversion); pPomFile = new File(getM2(), sb.toString() + ".pom"); // }else{ // if(relativePath.endsWith("/")||relativePath.endsWith("\\")){ // relativePath=relativePath.concat("pom.xml"); // }else if(relativePath.endsWith("pom.xml")){ // }else{ // relativePath=relativePath.concat("/pom.xml"); // } // pPomFile=new File(doc.pomFile.getParentFile(),relativePath); // } if (pPomFile.exists()) return new Pom(pPomFile,doc.isNative); } return null; } private static void parseExtends(ExtendsAsset parentContext, Pom doc) throws SAXException, IOException { List<Pom> parents = getParents(doc); for (Pom parent : parents) { Map<String, String> map = XMLUtils.getAttributesMap((Element) SimpleXPath.getNodeByXPath(parent.getDocument(), "/project/properties"), true); parentContext.properties.putAll(map); parseDependency(parentContext, parent); } } /** * 计算版本冲突,如果前面的版本不大于前面的版本,就返回true * * @param from * @param to * @return */ private static boolean canReplace(Dependency from, Dependency to) { String[] v1 = StringUtils.split(from.getVersion(), ".-"); String[] v2 = StringUtils.split(to.getVersion(), ".-"); for (int i = 0; i < Math.min(v1.length, v2.length); i++) { int vv1 = StringUtils.toInt(v1[i], 0); int vv2 = StringUtils.toInt(v2[i], 0); if (vv1 > vv2) return false; if (vv1 < vv2) return true; } if (v1.length > v2.length) { return false; } return true; } public static File getM2() { File m2 = null; String mavenRepo = System.getenv("M2_REPOSITORY"); if (StringUtils.isNotEmpty(mavenRepo)) {// 支持自定义的Mavan目录 m2 = new File(mavenRepo); Assert.isTrue(m2.exists(), "Can not locate .m2/repository folder!" + m2.getAbsolutePath()); } else { String home = System.getenv("USERPROFILE"); if (StringUtils.isEmpty(home)) {// 支持Linux环境的参数 home = System.getenv("HOME"); } m2 = new File(home, ".m2/repository"); Assert.isTrue(m2.exists(), "Can not locate .m2/repository folder!" + m2.getAbsolutePath() + ". if you have a custom repository path, set it into the env-variable 'M2_REPOSITORY'."); } return m2; } private static String processVersion(String version, Pom pom, Map<String, String> assets) throws SAXException, IOException { Assert.notNull(version); if (version.indexOf("${") == -1) { return version; } String paramName = org.apache.commons.lang.StringUtils.substringBetween(version, "${", "}"); String paramValue = null; Document doc = pom.getDocument(); if ("project.version".equals(paramName)) { try { paramValue = SimpleXPath.getAttributeByXPath(doc, "/project/version@#text"); } catch (NoSuchElementException e) { } if (StringUtils.isEmpty(paramValue)) { try { paramValue = SimpleXPath.getAttributeByXPath(doc, "/project/parent/version@#text"); } catch (NoSuchElementException e) { } } } else { paramValue = assets.get(paramName); if (paramValue == null) { Pom currentDoc = pom; while (currentDoc != null) { Element propertiesNode = (Element) SimpleXPath.getNodeByXPath(currentDoc.getDocument(), "/project/properties"); paramValue = XMLUtils.nodeText(propertiesNode, paramName); if (paramValue != null) break; currentDoc = getParentPomFile(currentDoc); } } } if (paramValue != null) { return version.replace("${" + paramName + "}", paramValue); } return version; } public static String getJarFileName(Dependency dep) { StringBuilder sb = new StringBuilder(); sb.append(dep.getGroupId().replace('.', '/')).append('/'); sb.append(dep.getArtifactId().replace('.', '.')); sb.append("/"); sb.append(dep.getVersion()).append('/').append(dep.getArtifactId()).append('-').append(dep.getVersion()); if (StringUtils.isNotEmpty(dep.getClassifier())) sb.append("-").append(dep.getClassifier()); sb.append(".jar"); return sb.toString(); } private static String getChildPomName(String groupId, String artifactId, String version) { StringBuilder sb = new StringBuilder(); sb.append(groupId.replace('.', '/')).append('/'); sb.append(artifactId.replace('.', '.')); sb.append("/"); sb.append(version).append('/').append(artifactId).append('-').append(version); sb.append(".pom"); return sb.toString(); } private static Set<Exclusion> parseExclusion(Element e) { Set<Exclusion> result = new HashSet<Exclusion>(); List<Element> nodes = XMLUtils.getElementsByTagNames(e, "exclusion"); for (Element anExclusionNode : nodes) { String gpId = XMLUtils.first(anExclusionNode, "groupId").getTextContent(); String arId = XMLUtils.first(anExclusionNode, "artifactId").getTextContent(); Exclusion anExclusion = new Exclusion(); result.add(anExclusion); anExclusion.setGroupId(gpId); anExclusion.setArtifactId(arId); } return result; } /** * 解析本级的依赖,和上级合并 * * @param parent * @param doc * @param assets * @return * @throws SAXException * @throws IOException */ private static void parseDependency(ExtendsAsset assets, Pom doc) throws SAXException, IOException { Element docEle = doc.getDocument().getDocumentElement(); for (Element dependenciesEle : XMLUtils.getElementsByTagNames(docEle, "dependencies")) { List<Element> dependEles = XMLUtils.childElements(dependenciesEle, "dependency"); if (dependEles == null) return; for (Element e : dependEles) { String groupId = XMLUtils.nodeText(e, "groupId"); String artifactId = XMLUtils.nodeText(e, "artifactId"); String version = XMLUtils.nodeText(e, "version"); if (groupId == null || artifactId == null || version == null) continue; String classifier = XMLUtils.nodeText(e, "classifier"); String scope = XMLUtils.nodeText(e, "scope"); if ("test".equals(scope) || "system".equals(scope)) {// 这些依赖无效 continue; } if ("provided".equals(scope) && doc.isNative == false) {// 非本级的provided依赖无效 continue; } version = processVersion(version, doc, assets.properties); Exclusions exclusions = new Exclusions(); Dependency d = new Dependency(); d.setGroupId(groupId); d.setArtifactId(artifactId); d.setVersion(version); d.setClassifier(classifier); d.setScope(scope); d.setExclusions(exclusions); Set<Exclusion> exclusionSet = parseExclusion(e); exclusions.getExclusion().addAll(exclusionSet); assets.add(d); } } } }