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 String tags = extractText(document, "/project/properties/tags"); 125 if (tags != null && tags.length() > 0) 126 { 127 info.setTags(Arrays.asList(tags.split(","))); 128 } 129 130 } 131 132 return info; 133 134 } 135 136 private String extractText(Document document, String xpathExpression) 137 { 138 XPath xpath = XPathFactory.newInstance().newXPath(); 139 String text; 140 try 141 { 142 XPathExpression expression = xpath.compile(xpathExpression); 143 text = (String) expression.evaluate(document, XPathConstants.STRING); 144 } 145 catch (XPathExpressionException e) 146 { 147 throw new RuntimeException(e); 148 } 149 if ("".equals(text)) 150 { 151 text = null; 152 } 153 return text; 154 } 155 156 private String getPomPath(LibraryMapping libraryMapping) 157 { 158 final String rootPackageConverted = libraryMapping.getRootPackage().replace('.', '/'); 159 final URL rootPackageUrl = getClass().getClassLoader().getResource(rootPackageConverted); 160 String path = rootPackageUrl.toString(); 161 String url = null; 162 if (path.contains("!/")) 163 { 164 path = path.substring(0, path.indexOf("!/")); 165 } 166 for (String pomPath : pomPaths) 167 { 168 if (path.equals(getPomPathUrl(pomPath))) { 169 url = pomPath; 170 break; 171 } 172 } 173 return url; 174 } 175 176 private String getPomPathUrl(String pomPath) 177 { 178 String url = pomPathToRootUrl.get(pomPath); 179 if (url == null) 180 { 181 for (String path : pomPaths) 182 { 183 final URL resource = getClass().getResource("/" + path); 184 String resourcePath = null; 185 if (resource != null && resource.toString().contains("!/")) 186 { 187 resourcePath = resource.toString(); 188 resourcePath = resourcePath.substring(0, resourcePath.indexOf("!/")); 189 } 190 pomPathToRootUrl.put(path, resourcePath); 191 url = resourcePath; 192 } 193 } 194 return url; 195 } 196 197 private static Set<String> findPomPaths(ClasspathScanner classpathScanner) 198 { 199 final ClasspathMatcher classpathMatcher = new ClasspathMatcher() 200 { 201 @Override 202 public boolean matches(String packagePath, String fileName) 203 { 204 return fileName.equals("pom.xml"); 205 } 206 }; 207 try 208 { 209 return classpathScanner.scan("META-INF/maven/", classpathMatcher); 210 } 211 catch (IOException e) 212 { 213 throw new RuntimeException("Exception while finding pom.xml files in the classpath", e); 214 } 215 } 216 217}