001// Copyright 2014 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014package org.apache.tapestry5.internal.services; 015 016import java.io.IOException; 017import java.io.InputStream; 018import java.net.URL; 019import java.util.Arrays; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.Map; 023import java.util.Set; 024import java.util.WeakHashMap; 025 026import javax.xml.parsers.DocumentBuilder; 027import javax.xml.parsers.DocumentBuilderFactory; 028import javax.xml.xpath.XPath; 029import javax.xml.xpath.XPathConstants; 030import javax.xml.xpath.XPathExpression; 031import javax.xml.xpath.XPathExpressionException; 032import javax.xml.xpath.XPathFactory; 033 034import org.apache.tapestry5.ioc.services.ClasspathMatcher; 035import org.apache.tapestry5.ioc.services.ClasspathScanner; 036import org.apache.tapestry5.services.ComponentLibraryInfo; 037import org.apache.tapestry5.services.ComponentLibraryInfoSource; 038import org.apache.tapestry5.services.LibraryMapping; 039import org.slf4j.Logger; 040import org.w3c.dom.Document; 041 042/** 043 * {@link ComponentLibraryInfoSource} implementation based on the pom.xml and pom.properties files 044 * Maven places in the /META-INF/maven/[groupId]/[artifactId] folder. 045 */ 046public class MavenComponentLibraryInfoSource implements ComponentLibraryInfoSource 047{ 048 049 final private Logger logger; 050 051 final private Set<String> pomPaths; 052 053 final private Map<String, ComponentLibraryInfo> cache = new HashMap<String, ComponentLibraryInfo>(); 054 055 final private Map<String, String> pomPathToRootUrl; 056 057 final private DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); 058 059 public MavenComponentLibraryInfoSource(Logger logger, ClasspathScanner classpathScanner) 060 { 061 super(); 062 this.logger = logger; 063 this.pomPaths = Collections.unmodifiableSet(findPomPaths(classpathScanner)); 064 pomPathToRootUrl = new WeakHashMap<String, String>(pomPaths.size()); 065 } 066 067 @Override 068 public ComponentLibraryInfo find(LibraryMapping libraryMapping) 069 { 070 ComponentLibraryInfo info = null; 071 if (cache.containsKey(libraryMapping.libraryName)) 072 { 073 info = cache.get(libraryMapping.libraryName); 074 } 075 else 076 { 077 final String pomPath = getPomPath(libraryMapping); 078 if (pomPath != null) 079 { 080 InputStream inputStream = getClass().getResourceAsStream("/" + pomPath); 081 info = parse(inputStream); 082 info.setLibraryMapping(libraryMapping); 083 cache.put(libraryMapping.libraryName, info); 084 } 085 else 086 { 087 cache.put(libraryMapping.libraryName, null); 088 } 089 } 090 return info; 091 } 092 093 private ComponentLibraryInfo parse(InputStream inputStream) 094 { 095 ComponentLibraryInfo info = null; 096 if (inputStream != null) 097 { 098 099 Document document; 100 101 try 102 { 103 DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); 104 document = documentBuilder.parse(inputStream); 105 } 106 catch (Exception e) 107 { 108 logger.warn("Exception while parsing pom.xml", e); 109 return null; 110 } 111 112 info = new ComponentLibraryInfo(); 113 info.setGroupId(extractText(document, "(/project/groupId | /project/parent/groupId)[1]")); 114 info.setArtifactId(extractText(document, "/project/artifactId")); 115 info.setVersion(extractText(document, "/project/version")); 116 info.setName(extractText(document, "/project/name")); 117 info.setDescription(extractText(document, "/project/description")); 118 info.setDocumentationUrl(extractText(document, "/project/properties/documentationUrl")); 119 info.setHomepageUrl(extractText(document, "/project/properties/homepageUrl")); 120 info.setIssueTrackerUrl(extractText(document, "/project/issueManagement/url")); 121 info.setJavadocUrl(extractText(document, "/project/properties/javadocUrl")); 122 info.setSourceBrowseUrl(extractText(document, "/project/scm/url")); 123 info.setSourceRootUrl(extractText(document, "/project/properties/sourceRootUrl")); 124 info.setTapestryVersion(extractText(document, "(/project/dependencies/dependency[./groupId='org.apache.tapestry'][./artifactId='tapestry-core']/version | /project/properties/tapestryVersion)[1]")); 125 String tags = extractText(document, "/project/properties/tags"); 126 if (tags != null && tags.length() > 0) 127 { 128 info.setTags(Arrays.asList(tags.split(","))); 129 } 130 131 } 132 133 return info; 134 135 } 136 137 private String extractText(Document document, String xpathExpression) 138 { 139 XPath xpath = XPathFactory.newInstance().newXPath(); 140 String text; 141 try 142 { 143 XPathExpression expression = xpath.compile(xpathExpression); 144 text = (String) expression.evaluate(document, XPathConstants.STRING); 145 } 146 catch (XPathExpressionException e) 147 { 148 throw new RuntimeException(e); 149 } 150 if ("".equals(text)) 151 { 152 text = null; 153 } 154 return text; 155 } 156 157 private String getPomPath(LibraryMapping libraryMapping) 158 { 159 final String rootPackageConverted = libraryMapping.getRootPackage().replace('.', '/'); 160 final URL rootPackageUrl = getClass().getClassLoader().getResource(rootPackageConverted); 161 String path = rootPackageUrl.toString(); 162 String url = null; 163 if (path.contains("!/")) 164 { 165 path = path.substring(0, path.indexOf("!/")); 166 } 167 for (String pomPath : pomPaths) 168 { 169 if (path.equals(getPomPathUrl(pomPath))) { 170 url = pomPath; 171 break; 172 } 173 } 174 return url; 175 } 176 177 private String getPomPathUrl(String pomPath) 178 { 179 String url = pomPathToRootUrl.get(pomPath); 180 if (url == null) 181 { 182 for (String path : pomPaths) 183 { 184 final URL resource = getClass().getResource("/" + path); 185 String resourcePath = null; 186 if (resource != null && resource.toString().contains("!/")) 187 { 188 resourcePath = resource.toString(); 189 resourcePath = resourcePath.substring(0, resourcePath.indexOf("!/")); 190 } 191 pomPathToRootUrl.put(path, resourcePath); 192 url = resourcePath; 193 } 194 } 195 return url; 196 } 197 198 private static Set<String> findPomPaths(ClasspathScanner classpathScanner) 199 { 200 final ClasspathMatcher classpathMatcher = new ClasspathMatcher() 201 { 202 @Override 203 public boolean matches(String packagePath, String fileName) 204 { 205 return fileName.equals("pom.xml"); 206 } 207 }; 208 try 209 { 210 return classpathScanner.scan("META-INF/maven/", classpathMatcher); 211 } 212 catch (IOException e) 213 { 214 throw new RuntimeException("Exception while finding pom.xml files in the classpath", e); 215 } 216 } 217 218}