1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.maven;
25
26 import org.modeshape.common.component.ClassLoaderFactory;
27 import org.modeshape.common.util.CheckArg;
28 import org.modeshape.common.util.Logger;
29 import org.modeshape.common.xml.SimpleNamespaceContext;
30 import org.modeshape.maven.spi.JcrMavenUrlProvider;
31 import org.modeshape.maven.spi.MavenUrlProvider;
32 import org.w3c.dom.Document;
33 import org.w3c.dom.NodeList;
34 import org.xml.sax.SAXException;
35
36 import javax.jcr.Repository;
37 import javax.xml.parsers.DocumentBuilder;
38 import javax.xml.parsers.DocumentBuilderFactory;
39 import javax.xml.parsers.ParserConfigurationException;
40 import javax.xml.xpath.XPath;
41 import javax.xml.xpath.XPathConstants;
42 import javax.xml.xpath.XPathExpression;
43 import javax.xml.xpath.XPathExpressionException;
44 import javax.xml.xpath.XPathFactory;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.net.MalformedURLException;
48 import java.net.URL;
49 import java.net.URLConnection;
50 import java.util.ArrayList;
51 import java.util.Collections;
52 import java.util.EnumSet;
53 import java.util.HashSet;
54 import java.util.Iterator;
55 import java.util.List;
56 import java.util.Set;
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public class MavenRepository implements ClassLoaderFactory {
72
73 private final MavenUrlProvider urlProvider;
74 private final MavenClassLoaders classLoaders;
75 private static final Logger LOGGER = Logger.getLogger(MavenRepository.class);
76
77 public MavenRepository( final MavenUrlProvider urlProvider ) {
78 CheckArg.isNotNull(urlProvider, "urlProvider");
79 this.urlProvider = urlProvider;
80 this.classLoaders = new MavenClassLoaders(this);
81 assert LOGGER != null;
82 assert this.urlProvider != null;
83 }
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 public ClassLoader getClassLoader( ClassLoader parent,
99 MavenId... mavenIds ) {
100 CheckArg.isNotEmpty(mavenIds, "mavenIds");
101 CheckArg.containsNoNulls(mavenIds, "mavenIds");
102 return this.classLoaders.getClassLoader(parent, mavenIds);
103 }
104
105
106
107
108
109
110
111
112
113 public ClassLoader getClassLoader( String... coordinates ) {
114 return getClassLoader(null, coordinates);
115 }
116
117
118
119
120
121
122
123
124
125
126
127
128 public ClassLoader getClassLoader( ClassLoader parent,
129 String... coordinates ) {
130 CheckArg.isNotEmpty(coordinates, "coordinates");
131 CheckArg.containsNoNulls(coordinates, "coordinates");
132 MavenId[] mavenIds = new MavenId[coordinates.length];
133 for (int i = 0; i < coordinates.length; i++) {
134 String coordinate = coordinates[i];
135 mavenIds[i] = new MavenId(coordinate);
136 }
137 return getClassLoader(parent, mavenIds);
138 }
139
140
141
142
143
144
145
146
147
148 public boolean exists( MavenId mavenId ) throws MavenRepositoryException {
149 if (mavenId == null) return false;
150 Set<MavenId> existing = exists(mavenId, (MavenId)null);
151 return existing.contains(mavenId);
152 }
153
154
155
156
157
158
159
160
161
162
163 public Set<MavenId> exists( MavenId firstId,
164 MavenId... mavenIds ) throws MavenRepositoryException {
165 if (mavenIds == null || mavenIds.length == 0) return Collections.emptySet();
166
167
168 Set<MavenId> nonNullIds = new HashSet<MavenId>();
169 if (firstId != null) nonNullIds.add(firstId);
170 for (MavenId mavenId : mavenIds) {
171 if (mavenId != null) nonNullIds.add(mavenId);
172 }
173 if (nonNullIds.isEmpty()) return nonNullIds;
174
175 MavenId lastMavenId = null;
176 try {
177 for (Iterator<MavenId> iter = nonNullIds.iterator(); iter.hasNext();) {
178 lastMavenId = iter.next();
179 URL urlToMavenId = this.urlProvider.getUrl(lastMavenId, null, null, false);
180 boolean exists = urlToMavenId != null;
181 if (!exists) iter.remove();
182 }
183 } catch (MalformedURLException err) {
184 throw new MavenRepositoryException(MavenI18n.errorCreatingUrlForMavenId.text(lastMavenId, err.getMessage()));
185 }
186 return nonNullIds;
187 }
188
189
190
191
192
193
194
195
196
197
198
199
200 public List<MavenDependency> getDependencies( MavenId mavenId ) {
201 URL pomUrl = null;
202 try {
203 pomUrl = getUrl(mavenId, ArtifactType.POM, null);
204 return getDependencies(mavenId, pomUrl.openStream());
205 } catch (IOException e) {
206 throw new MavenRepositoryException(MavenI18n.errorGettingPomFileForMavenIdAtUrl.text(mavenId, pomUrl), e);
207 }
208 }
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225 protected List<MavenDependency> getDependencies( MavenId mavenId,
226 InputStream pomStream,
227 MavenDependency.Scope... allowedScopes ) throws IOException {
228 CheckArg.isNotNull(mavenId, "mavenId");
229 CheckArg.isNotNull(pomStream, "pomStream");
230 EnumSet<MavenDependency.Scope> includedScopes = MavenDependency.Scope.getRuntimeScopes();
231 if (allowedScopes != null && allowedScopes.length > 0) includedScopes = EnumSet.of(allowedScopes[0], allowedScopes);
232 List<MavenDependency> results = new ArrayList<MavenDependency>();
233
234 try {
235
236 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
237 factory.setNamespaceAware(true);
238 DocumentBuilder builder = factory.newDocumentBuilder();
239 Document doc = builder.parse(pomStream);
240
241
242 XPathFactory xpathFactory = XPathFactory.newInstance();
243 XPath xpath = xpathFactory.newXPath();
244 xpath.setNamespaceContext(new SimpleNamespaceContext().setNamespace("m", "http://maven.apache.org/POM/4.0.0"));
245
246
247 XPathExpression projectExpression = xpath.compile("//m:project");
248 XPathExpression dependencyExpression = xpath.compile("//m:project/m:dependencies/m:dependency");
249 XPathExpression groupIdExpression = xpath.compile("./m:groupId/text()");
250 XPathExpression artifactIdExpression = xpath.compile("./m:artifactId/text()");
251 XPathExpression versionExpression = xpath.compile("./m:version/text()");
252 XPathExpression classifierExpression = xpath.compile("./m:classifier/text()");
253 XPathExpression scopeExpression = xpath.compile("./m:scope/text()");
254 XPathExpression typeExpression = xpath.compile("./m:type/text()");
255 XPathExpression exclusionExpression = xpath.compile("./m:exclusions/m:exclusion");
256
257
258 org.w3c.dom.Node projectNode = (org.w3c.dom.Node)projectExpression.evaluate(doc, XPathConstants.NODE);
259 String groupId = (String)groupIdExpression.evaluate(projectNode, XPathConstants.STRING);
260 String artifactId = (String)artifactIdExpression.evaluate(projectNode, XPathConstants.STRING);
261 String version = (String)versionExpression.evaluate(projectNode, XPathConstants.STRING);
262 String classifier = (String)classifierExpression.evaluate(projectNode, XPathConstants.STRING);
263 if (groupId == null || artifactId == null || version == null) {
264 throw new IllegalArgumentException(MavenI18n.pomFileIsInvalid.text(mavenId));
265 }
266 MavenId actualMavenId = new MavenId(groupId, artifactId, version, classifier);
267 if (!mavenId.equals(actualMavenId)) {
268 throw new IllegalArgumentException(MavenI18n.pomFileContainsUnexpectedId.text(actualMavenId, mavenId));
269 }
270
271
272 NodeList nodes = (NodeList)dependencyExpression.evaluate(doc, XPathConstants.NODESET);
273 for (int i = 0; i < nodes.getLength(); ++i) {
274 org.w3c.dom.Node dependencyNode = nodes.item(i);
275 assert dependencyNode != null;
276 String depGroupId = (String)groupIdExpression.evaluate(dependencyNode, XPathConstants.STRING);
277 String depArtifactId = (String)artifactIdExpression.evaluate(dependencyNode, XPathConstants.STRING);
278 String depVersion = (String)versionExpression.evaluate(dependencyNode, XPathConstants.STRING);
279 String depClassifier = (String)classifierExpression.evaluate(dependencyNode, XPathConstants.STRING);
280 String scopeText = (String)scopeExpression.evaluate(dependencyNode, XPathConstants.STRING);
281 String depType = (String)typeExpression.evaluate(dependencyNode, XPathConstants.STRING);
282
283
284 if (depGroupId == null || depArtifactId == null || depVersion == null) {
285 LOGGER.trace("Skipping dependency of {1} due to missing groupId, artifactId or version: {2}",
286 mavenId,
287 dependencyNode);
288 continue;
289 }
290 MavenDependency dependency = new MavenDependency(depGroupId, depArtifactId, depVersion, depClassifier);
291 dependency.setType(depType);
292
293
294 dependency.setScope(scopeText);
295 if (!includedScopes.contains(dependency.getScope())) continue;
296
297
298 NodeList exclusionNodes = (NodeList)exclusionExpression.evaluate(dependencyNode, XPathConstants.NODESET);
299 for (int j = 0; j < exclusionNodes.getLength(); ++j) {
300 org.w3c.dom.Node exclusionNode = exclusionNodes.item(j);
301 assert exclusionNode != null;
302 String excludedGroupId = (String)groupIdExpression.evaluate(exclusionNode, XPathConstants.STRING);
303 String excludedArtifactId = (String)artifactIdExpression.evaluate(exclusionNode, XPathConstants.STRING);
304
305 if (excludedGroupId == null || excludedArtifactId == null) {
306 LOGGER.trace("Skipping exclusion in dependency of {1} due to missing exclusion groupId or artifactId: {2} ",
307 mavenId,
308 exclusionNode);
309 continue;
310 }
311 MavenId excludedId = new MavenId(excludedGroupId, excludedArtifactId);
312 dependency.getExclusions().add(excludedId);
313 }
314
315 results.add(dependency);
316 }
317 } catch (XPathExpressionException err) {
318 throw new MavenRepositoryException(MavenI18n.errorCreatingXpathStatementsToEvaluatePom.text(mavenId), err);
319 } catch (ParserConfigurationException err) {
320 throw new MavenRepositoryException(MavenI18n.errorCreatingXpathParserToEvaluatePom.text(mavenId), err);
321 } catch (SAXException err) {
322 throw new MavenRepositoryException(MavenI18n.errorReadingXmlDocumentToEvaluatePom.text(mavenId), err);
323 } finally {
324 try {
325 pomStream.close();
326 } catch (IOException e) {
327 LOGGER.error(e, MavenI18n.errorClosingUrlStreamToPom, mavenId);
328 }
329 }
330 return results;
331 }
332
333
334
335
336
337
338
339
340
341
342
343
344 public URL getUrl( MavenId mavenId,
345 ArtifactType artifactType,
346 SignatureType signatureType ) throws MalformedURLException {
347 return this.urlProvider.getUrl(mavenId, artifactType, signatureType, false);
348 }
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363 public URL getUrl( MavenId mavenId,
364 ArtifactType artifactType,
365 SignatureType signatureType,
366 boolean createIfRequired ) throws MalformedURLException {
367 return this.urlProvider.getUrl(mavenId, artifactType, signatureType, createIfRequired);
368 }
369
370
371
372
373
374
375
376 protected void notifyUpdatedPom( MavenId mavenId ) {
377 this.classLoaders.notifyChangeInDependencies(mavenId);
378 }
379 }