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