]> git.argeo.org Git - gpl/argeo-slc.git/blob - org.argeo.slc.repo/src/org/argeo/slc/repo/maven/GenerateBinaries.java
Merge remote-tracking branch 'origin/master' into testing
[gpl/argeo-slc.git] / org.argeo.slc.repo / src / org / argeo / slc / repo / maven / GenerateBinaries.java
1 package org.argeo.slc.repo.maven;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.Set;
6 import java.util.StringTokenizer;
7 import java.util.TreeSet;
8
9 import javax.jcr.Credentials;
10 import javax.jcr.Node;
11 import javax.jcr.NodeIterator;
12 import javax.jcr.Repository;
13 import javax.jcr.RepositoryException;
14 import javax.jcr.Session;
15
16 import org.apache.commons.logging.Log;
17 import org.apache.commons.logging.LogFactory;
18 import org.argeo.jcr.JcrMonitor;
19 import org.argeo.jcr.JcrUtils;
20 import org.argeo.slc.SlcException;
21 import org.argeo.slc.SlcNames;
22 import org.argeo.slc.SlcTypes;
23 import org.argeo.slc.repo.ArtifactIndexer;
24 import org.argeo.slc.repo.RepoConstants;
25 import org.argeo.slc.repo.RepoUtils;
26 import org.eclipse.aether.artifact.Artifact;
27 import org.eclipse.aether.artifact.DefaultArtifact;
28 import org.osgi.framework.Version;
29
30 /**
31 * Generates binaries-, sources- and sdk-version.pom artifacts for a given
32 * group.
33 */
34 public class GenerateBinaries implements Runnable, SlcNames {
35 private final static Log log = LogFactory.getLog(GenerateBinaries.class);
36
37 // Connection info
38 private Repository repository;
39 private Credentials credentials;
40 private String workspace;
41
42 // Business info
43 private String groupId;
44 private String parentPomCoordinates;
45 private String version = null;
46
47 // Constants
48 private String artifactBasePath = RepoConstants.DEFAULT_ARTIFACTS_BASE_PATH;
49 private List<String> excludedSuffixes = new ArrayList<String>();
50
51 // Indexes
52 private Set<Artifact> binaries = new TreeSet<Artifact>(
53 new ArtifactIdComparator());
54 private Set<Artifact> sources = new TreeSet<Artifact>(
55 new ArtifactIdComparator());
56
57 // local cache
58 private ArtifactIndexer artifactIndexer = new ArtifactIndexer();
59 private Node allArtifactsHighestVersion;
60
61 public void run() {
62 Session session = null;
63 try {
64 session = repository.login(credentials, workspace);
65 Node groupNode = session.getNode(MavenConventionsUtils.groupPath(
66 artifactBasePath, groupId));
67 internalPreProcessing(groupNode, null);
68 internalProcessing(groupNode, null);
69 } catch (Exception e) {
70 throw new SlcException("Cannot normalize group " + groupId + " in "
71 + workspace, e);
72 } finally {
73 JcrUtils.logoutQuietly(session);
74 }
75 }
76
77 /**
78 * Generates binaries-, sources- and sdk-version.pom artifacts for the given
79 * version (or the highest of all children version if none is precised).
80 *
81 * By default, it includes each latest version of all artifact of this
82 * group.
83 *
84 * The 3 generated artifacts are then marked as modular distributions and
85 * indexed.
86 */
87 public static void processGroupNode(Node groupNode, String version,
88 JcrMonitor monitor) throws RepositoryException {
89 // TODO set artifactsBase based on group node
90 GenerateBinaries gb = new GenerateBinaries();
91 String groupId = groupNode.getProperty(SlcNames.SLC_GROUP_BASE_ID)
92 .getString();
93 gb.setGroupId(groupId);
94 gb.setVersion(version);
95 // TODO use already done pre-processing
96 gb.internalPreProcessing(groupNode, monitor);
97 gb.internalProcessing(groupNode, monitor);
98 }
99
100 /** Only builds local indexes. Does not change anything in the local Session */
101 public static GenerateBinaries preProcessGroupNode(Node groupNode,
102 JcrMonitor monitor) throws RepositoryException {
103 // TODO set artifactsBase based on group node
104 GenerateBinaries gb = new GenerateBinaries();
105 String groupId = groupNode.getProperty(SlcNames.SLC_GROUP_BASE_ID)
106 .getString();
107 gb.setGroupId(groupId);
108 // gb.setVersion(version);
109 // gb.setOverridePoms(overridePoms);
110 gb.internalPreProcessing(groupNode, monitor);
111 return gb;
112 }
113
114 // exposes indexes. to display results of the pre-processing phase.
115 public Set<Artifact> getBinaries() {
116 return binaries;
117 }
118
119 public Artifact getHighestArtifactVersion() throws RepositoryException {
120 return allArtifactsHighestVersion == null ? null : RepoUtils
121 .asArtifact(allArtifactsHighestVersion);
122 }
123
124 // //////////////////////////////////////
125 // INTERNAL METHODS
126
127 /**
128 * Browse all children of a Node considered as a folder that follows Aether
129 * conventions i.e that has Aether's artifact base as children.
130 *
131 * Each of such child contains a set of Aether artifact versions. This
132 * methods build the binaries {@code Set<Artifact>} and other indexes. It
133 * does not impact the
134 */
135 protected void internalPreProcessing(Node groupNode, JcrMonitor monitor)
136 throws RepositoryException {
137 if (monitor != null)
138 monitor.subTask("Pre processing group " + groupId);
139
140 // Process all direct children nodes,
141 // gathering latest versions of each artifact
142 allArtifactsHighestVersion = null;
143
144 aBases: for (NodeIterator aBases = groupNode.getNodes(); aBases
145 .hasNext();) {
146 Node aBase = aBases.nextNode();
147 if (aBase.isNodeType(SlcTypes.SLC_ARTIFACT_BASE)) {
148 Node highestAVersion = getArtifactLatestVersion(aBase);
149 if (highestAVersion == null)
150 continue aBases;
151 else {
152 // retrieve relevant child node
153 // Information is stored on the NT_FILE child node.
154 for (NodeIterator files = highestAVersion.getNodes(); files
155 .hasNext();) {
156 Node file = files.nextNode();
157 if (file.isNodeType(SlcTypes.SLC_BUNDLE_ARTIFACT)) {
158 if (log.isDebugEnabled())
159 log.debug("Pre-Processing " + file.getName());
160 preProcessBundleArtifact(file);
161 }
162 }
163 }
164 }
165 }
166 // if (log.isDebugEnabled()) {
167 // int bundleCount = symbolicNamesToNodes.size();
168 // log.debug("" + bundleCount + " bundles have been indexed for "
169 // + groupId);
170 // }
171 }
172
173 /** Does the real job : writes JCR META-DATA and generates binaries */
174 protected void internalProcessing(Node groupNode, JcrMonitor monitor)
175 throws RepositoryException {
176 if (monitor != null)
177 monitor.subTask("Processing group " + groupId);
178
179 Session session = groupNode.getSession();
180
181 // if version not set or empty, use the highest version
182 // useful when indexing a product maven repository where
183 // all artifacts have the same version for a given release
184 // => the version can then be left empty
185 if (version == null || version.trim().equals(""))
186 if (allArtifactsHighestVersion != null)
187 version = allArtifactsHighestVersion.getProperty(
188 SLC_ARTIFACT_VERSION).getString();
189 else
190 throw new SlcException("Group version " + version
191 + " is empty.");
192
193 // int bundleCount = symbolicNamesToNodes.size();
194 // int count = 1;
195 // for (Node bundleNode : symbolicNamesToNodes.values()) {
196 // if (log.isDebugEnabled())
197 // log.debug("Processing " + bundleNode.getName() + " ( " + count
198 // + "/" + bundleCount + " )");
199 //
200 // // processBundleArtifact(bundleNode);
201 // // bundleNode.getSession().save();
202 // count++;
203 // }
204
205 // indexes
206 Set<Artifact> indexes = new TreeSet<Artifact>(
207 new ArtifactIdComparator());
208
209 Artifact indexArtifact;
210 indexArtifact = writeIndex(session, RepoConstants.BINARIES_ARTIFACT_ID,
211 binaries);
212 indexes.add(indexArtifact);
213
214 indexArtifact = writeIndex(session, RepoConstants.SOURCES_ARTIFACT_ID,
215 sources);
216 indexes.add(indexArtifact);
217
218 // sdk
219 writeIndex(session, RepoConstants.SDK_ARTIFACT_ID, indexes);
220
221 if (monitor != null)
222 monitor.worked(1);
223 }
224
225 protected void preProcessBundleArtifact(Node bundleNode)
226 throws RepositoryException {
227
228 String symbolicName = JcrUtils.get(bundleNode, SLC_SYMBOLIC_NAME);
229 // Sanity check.
230 if (symbolicName == null)
231 log.warn("Symbolic name is null for bundle " + bundleNode);
232
233 // Manage source bundles
234 if (symbolicName.endsWith(".source")) {
235 // TODO make a shared node with classifier 'sources'?
236 String bundleName = RepoUtils
237 .extractBundleNameFromSourceName(symbolicName);
238 for (String excludedSuffix : excludedSuffixes) {
239 if (bundleName.endsWith(excludedSuffix))
240 return;// skip adding to sources
241 }
242 sources.add(RepoUtils.asArtifact(bundleNode));
243 return;
244 }
245
246 // // Build indexes
247 // NodeIterator exportPackages = bundleNode.getNodes(SLC_
248 // + Constants.EXPORT_PACKAGE);
249 // while (exportPackages.hasNext()) {
250 // Node exportPackage = exportPackages.nextNode();
251 // String pkg = JcrUtils.get(exportPackage, SLC_NAME);
252 // packagesToSymbolicNames.put(pkg, symbolicName);
253 // }
254 //
255 // symbolicNamesToNodes.put(symbolicName, bundleNode);
256 // for (String excludedSuffix : excludedSuffixes) {
257 // if (symbolicName.endsWith(excludedSuffix))
258 // return;// skip adding to binaries
259 // }
260
261 binaries.add(RepoUtils.asArtifact(bundleNode));
262
263 // Extra check. to remove
264 if (bundleNode.getSession().hasPendingChanges())
265 throw new SlcException("Pending changes in the session, "
266 + "this should not be true here.");
267 }
268
269 // protected void processBundleArtifact(Node bundleNode)
270 // throws RepositoryException {
271 // Node artifactFolder = bundleNode.getParent();
272 // String baseName = FilenameUtils.getBaseName(bundleNode.getName());
273 //
274 // // pom
275 // String pomName = baseName + ".pom";
276 // if (artifactFolder.hasNode(pomName) && !overridePoms)
277 // return;// skip
278 //
279 // String pom = generatePomForBundle(bundleNode);
280 // Node pomNode = JcrUtils.copyBytesAsFile(artifactFolder, pomName,
281 // pom.getBytes());
282 // // checksum
283 // String bundleSha = JcrUtils.checksumFile(bundleNode, "SHA-1");
284 // JcrUtils.copyBytesAsFile(artifactFolder,
285 // bundleNode.getName() + ".sha1", bundleSha.getBytes());
286 // String pomSha = JcrUtils.checksumFile(pomNode, "SHA-1");
287 // JcrUtils.copyBytesAsFile(artifactFolder, pomNode.getName() + ".sha1",
288 // pomSha.getBytes());
289 // }
290
291 // ////////////////////
292 // LOCAL WRITERS
293 //
294
295 private Artifact writeIndex(Session session, String artifactId,
296 Set<Artifact> artifacts) throws RepositoryException {
297 Artifact artifact = new DefaultArtifact(groupId, artifactId, "pom",
298 version);
299 Artifact parentArtifact = parentPomCoordinates != null ? new DefaultArtifact(
300 parentPomCoordinates) : null;
301 String pom = MavenConventionsUtils.artifactsAsDependencyPom(artifact,
302 artifacts, parentArtifact);
303 Node node = RepoUtils.copyBytesAsArtifact(
304 session.getNode(artifactBasePath), artifact, pom.getBytes());
305 artifactIndexer.index(node);
306
307 // TODO factorize
308 String pomSha = JcrUtils.checksumFile(node, "SHA-1");
309 JcrUtils.copyBytesAsFile(node.getParent(), node.getName() + ".sha1",
310 pomSha.getBytes());
311 String pomMd5 = JcrUtils.checksumFile(node, "MD5");
312 JcrUtils.copyBytesAsFile(node.getParent(), node.getName() + ".md5",
313 pomMd5.getBytes());
314 session.save();
315 return artifact;
316 }
317
318 // Helpers
319 private Node getArtifactLatestVersion(Node artifactBase) {
320 try {
321 Node highestAVersion = null;
322 for (NodeIterator aVersions = artifactBase.getNodes(); aVersions
323 .hasNext();) {
324 Node aVersion = aVersions.nextNode();
325 if (aVersion.isNodeType(SlcTypes.SLC_ARTIFACT_VERSION_BASE)) {
326 if (highestAVersion == null) {
327 highestAVersion = aVersion;
328 if (allArtifactsHighestVersion == null)
329 allArtifactsHighestVersion = aVersion;
330 // Correctly handle following arrival order:
331 // Name1 - V1, name2 - V3
332 else {
333 Version cachedHighestVersion = extractOsgiVersion(allArtifactsHighestVersion);
334 Version currVersion = extractOsgiVersion(aVersion);
335 if (currVersion.compareTo(cachedHighestVersion) > 0)
336 allArtifactsHighestVersion = aVersion;
337 }
338 } else {
339 Version currVersion = extractOsgiVersion(aVersion);
340 Version currentHighestVersion = extractOsgiVersion(highestAVersion);
341 if (currVersion.compareTo(currentHighestVersion) > 0) {
342 highestAVersion = aVersion;
343 }
344 if (currVersion
345 .compareTo(extractOsgiVersion(allArtifactsHighestVersion)) > 0) {
346 allArtifactsHighestVersion = aVersion;
347 }
348 }
349
350 }
351 }
352 return highestAVersion;
353 } catch (RepositoryException re) {
354 throw new SlcException("Unable to get latest version for node "
355 + artifactBase, re);
356 }
357 }
358
359 private Version extractOsgiVersion(Node artifactVersion)
360 throws RepositoryException {
361 String rawVersion = artifactVersion.getProperty(SLC_ARTIFACT_VERSION)
362 .getString();
363 String cleanVersion = rawVersion.replace("-SNAPSHOT", ".SNAPSHOT");
364 Version osgiVersion = null;
365 // log invalid version value to enable tracking them
366 try {
367 osgiVersion = new Version(cleanVersion);
368 } catch (IllegalArgumentException e) {
369 log.error("Version string " + cleanVersion + " is invalid ");
370 String twickedVersion = twickInvalidVersion(cleanVersion);
371 osgiVersion = new Version(twickedVersion);
372 log.error("Using " + twickedVersion + " instead");
373 // throw e;
374 }
375 return osgiVersion;
376 }
377
378 private String twickInvalidVersion(String tmpVersion) {
379 String[] tokens = tmpVersion.split("\\.");
380 if (tokens.length == 3 && tokens[2].lastIndexOf("-") > 0) {
381 String newSuffix = tokens[2].replaceFirst("-", ".");
382 tmpVersion = tmpVersion.replaceFirst(tokens[2], newSuffix);
383 } else if (tokens.length > 4) {
384 // FIXME manually remove other "."
385 StringTokenizer st = new StringTokenizer(tmpVersion, ".", true);
386 StringBuilder builder = new StringBuilder();
387 // Major
388 builder.append(st.nextToken()).append(st.nextToken());
389 // Minor
390 builder.append(st.nextToken()).append(st.nextToken());
391 // Micro
392 builder.append(st.nextToken()).append(st.nextToken());
393 // Qualifier
394 builder.append(st.nextToken());
395 while (st.hasMoreTokens()) {
396 // consume delimiter
397 st.nextToken();
398 if (st.hasMoreTokens())
399 builder.append("-").append(st.nextToken());
400 }
401 tmpVersion = builder.toString();
402 }
403 return tmpVersion;
404 }
405
406 // private String generatePomForBundle(Node n) throws RepositoryException {
407 // String ownSymbolicName = JcrUtils.get(n, SLC_SYMBOLIC_NAME);
408 //
409 // StringBuffer p = new StringBuffer();
410 //
411 // // XML header
412 // p.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
413 // p.append("<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n");
414 // p.append("<modelVersion>4.0.0</modelVersion>");
415 //
416 // // Artifact
417 // p.append("<groupId>").append(JcrUtils.get(n, SLC_GROUP_ID))
418 // .append("</groupId>\n");
419 // p.append("<artifactId>").append(JcrUtils.get(n, SLC_ARTIFACT_ID))
420 // .append("</artifactId>\n");
421 // p.append("<version>").append(JcrUtils.get(n, SLC_ARTIFACT_VERSION))
422 // .append("</version>\n");
423 // p.append("<packaging>pom</packaging>\n");
424 // if (n.hasProperty(SLC_ + Constants.BUNDLE_NAME))
425 // p.append("<name>")
426 // .append(JcrUtils.get(n, SLC_ + Constants.BUNDLE_NAME))
427 // .append("</name>\n");
428 // if (n.hasProperty(SLC_ + Constants.BUNDLE_DESCRIPTION))
429 // p.append("<description>")
430 // .append(JcrUtils
431 // .get(n, SLC_ + Constants.BUNDLE_DESCRIPTION))
432 // .append("</description>\n");
433 //
434 // // Dependencies
435 // Set<String> dependenciesSymbolicNames = new TreeSet<String>();
436 // Set<String> optionalSymbolicNames = new TreeSet<String>();
437 // NodeIterator importPackages = n.getNodes(SLC_
438 // + Constants.IMPORT_PACKAGE);
439 // while (importPackages.hasNext()) {
440 // Node importPackage = importPackages.nextNode();
441 // String pkg = JcrUtils.get(importPackage, SLC_NAME);
442 // if (packagesToSymbolicNames.containsKey(pkg)) {
443 // String dependencySymbolicName = packagesToSymbolicNames
444 // .get(pkg);
445 // if (JcrUtils.check(importPackage, SLC_OPTIONAL))
446 // optionalSymbolicNames.add(dependencySymbolicName);
447 // else
448 // dependenciesSymbolicNames.add(dependencySymbolicName);
449 // } else {
450 // if (!JcrUtils.check(importPackage, SLC_OPTIONAL)
451 // && !systemPackages.contains(pkg))
452 // log.warn("No bundle found for pkg " + pkg);
453 // }
454 // }
455 //
456 // if (n.hasNode(SLC_ + Constants.FRAGMENT_HOST)) {
457 // String fragmentHost = JcrUtils.get(
458 // n.getNode(SLC_ + Constants.FRAGMENT_HOST),
459 // SLC_SYMBOLIC_NAME);
460 // dependenciesSymbolicNames.add(fragmentHost);
461 // }
462 //
463 // // TODO require bundles
464 //
465 // List<Node> dependencyNodes = new ArrayList<Node>();
466 // for (String depSymbName : dependenciesSymbolicNames) {
467 // if (depSymbName.equals(ownSymbolicName))
468 // continue;// skip self
469 //
470 // if (symbolicNamesToNodes.containsKey(depSymbName))
471 // dependencyNodes.add(symbolicNamesToNodes.get(depSymbName));
472 // else
473 // log.warn("Could not find node for " + depSymbName);
474 // }
475 // List<Node> optionalDependencyNodes = new ArrayList<Node>();
476 // for (String depSymbName : optionalSymbolicNames) {
477 // if (symbolicNamesToNodes.containsKey(depSymbName))
478 // optionalDependencyNodes.add(symbolicNamesToNodes
479 // .get(depSymbName));
480 // else
481 // log.warn("Could not find node for " + depSymbName);
482 // }
483 //
484 // p.append("<dependencies>\n");
485 // for (Node dependencyNode : dependencyNodes) {
486 // p.append("<dependency>\n");
487 // p.append("\t<groupId>")
488 // .append(JcrUtils.get(dependencyNode, SLC_GROUP_ID))
489 // .append("</groupId>\n");
490 // p.append("\t<artifactId>")
491 // .append(JcrUtils.get(dependencyNode, SLC_ARTIFACT_ID))
492 // .append("</artifactId>\n");
493 // p.append("</dependency>\n");
494 // }
495 //
496 // if (optionalDependencyNodes.size() > 0)
497 // p.append("<!-- OPTIONAL -->\n");
498 // for (Node dependencyNode : optionalDependencyNodes) {
499 // p.append("<dependency>\n");
500 // p.append("\t<groupId>")
501 // .append(JcrUtils.get(dependencyNode, SLC_GROUP_ID))
502 // .append("</groupId>\n");
503 // p.append("\t<artifactId>")
504 // .append(JcrUtils.get(dependencyNode, SLC_ARTIFACT_ID))
505 // .append("</artifactId>\n");
506 // p.append("\t<optional>true</optional>\n");
507 // p.append("</dependency>\n");
508 // }
509 // p.append("</dependencies>\n");
510 //
511 // // Dependency management
512 // p.append("<dependencyManagement>\n");
513 // p.append("<dependencies>\n");
514 // p.append("<dependency>\n");
515 // p.append("\t<groupId>").append(groupId).append("</groupId>\n");
516 // p.append("\t<artifactId>")
517 // .append(ownSymbolicName.endsWith(".source") ?
518 // RepoConstants.SOURCES_ARTIFACT_ID
519 // : RepoConstants.BINARIES_ARTIFACT_ID)
520 // .append("</artifactId>\n");
521 // p.append("\t<version>").append(version).append("</version>\n");
522 // p.append("\t<type>pom</type>\n");
523 // p.append("\t<scope>import</scope>\n");
524 // p.append("</dependency>\n");
525 // p.append("</dependencies>\n");
526 // p.append("</dependencyManagement>\n");
527 //
528 // p.append("</project>\n");
529 // return p.toString();
530 // }
531
532 /* SETTERS */
533 public void setRepository(Repository repository) {
534 this.repository = repository;
535 }
536
537 public void setCredentials(Credentials credentials) {
538 this.credentials = credentials;
539 }
540
541 public void setWorkspace(String workspace) {
542 this.workspace = workspace;
543 }
544
545 public void setGroupId(String groupId) {
546 this.groupId = groupId;
547 }
548
549 public void setParentPomCoordinates(String parentPomCoordinates) {
550 this.parentPomCoordinates = parentPomCoordinates;
551 }
552
553 public void setArtifactBasePath(String artifactBasePath) {
554 this.artifactBasePath = artifactBasePath;
555 }
556
557 public void setVersion(String version) {
558 this.version = version;
559 }
560
561 public void setExcludedSuffixes(List<String> excludedSuffixes) {
562 this.excludedSuffixes = excludedSuffixes;
563 }
564
565 public void setArtifactIndexer(ArtifactIndexer artifactIndexer) {
566 this.artifactIndexer = artifactIndexer;
567 }
568 }