1 package org
.argeo
.slc
.repo
.maven
;
3 import java
.util
.ArrayList
;
6 import java
.util
.StringTokenizer
;
7 import java
.util
.TreeSet
;
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
;
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
;
31 * Generates binaries-, sources- and sdk-version.pom artifacts for a given
34 public class GenerateBinaries
implements Runnable
, SlcNames
{
35 private final static Log log
= LogFactory
.getLog(GenerateBinaries
.class);
38 private Repository repository
;
39 private Credentials credentials
;
40 private String workspace
;
43 private String groupId
;
44 private String parentPomCoordinates
;
45 private String version
= null;
48 private String artifactBasePath
= RepoConstants
.DEFAULT_ARTIFACTS_BASE_PATH
;
49 private List
<String
> excludedSuffixes
= new ArrayList
<String
>();
52 private Set
<Artifact
> binaries
= new TreeSet
<Artifact
>(
53 new ArtifactIdComparator());
54 private Set
<Artifact
> sources
= new TreeSet
<Artifact
>(
55 new ArtifactIdComparator());
58 private ArtifactIndexer artifactIndexer
= new ArtifactIndexer();
59 private Node allArtifactsHighestVersion
;
62 Session session
= null;
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 "
73 JcrUtils
.logoutQuietly(session
);
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).
81 * By default, it includes each latest version of all artifact of this
84 * The 3 generated artifacts are then marked as modular distributions and
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
)
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
);
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
)
107 gb
.setGroupId(groupId
);
108 // gb.setVersion(version);
109 // gb.setOverridePoms(overridePoms);
110 gb
.internalPreProcessing(groupNode
, monitor
);
114 // exposes indexes. to display results of the pre-processing phase.
115 public Set
<Artifact
> getBinaries() {
119 public Artifact
getHighestArtifactVersion() throws RepositoryException
{
120 return allArtifactsHighestVersion
== null ?
null : RepoUtils
121 .asArtifact(allArtifactsHighestVersion
);
124 // //////////////////////////////////////
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.
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
135 protected void internalPreProcessing(Node groupNode
, JcrMonitor monitor
)
136 throws RepositoryException
{
138 monitor
.subTask("Pre processing group " + groupId
);
140 // Process all direct children nodes,
141 // gathering latest versions of each artifact
142 allArtifactsHighestVersion
= null;
144 aBases
: for (NodeIterator aBases
= groupNode
.getNodes(); aBases
146 Node aBase
= aBases
.nextNode();
147 if (aBase
.isNodeType(SlcTypes
.SLC_ARTIFACT_BASE
)) {
148 Node highestAVersion
= getArtifactLatestVersion(aBase
);
149 if (highestAVersion
== null)
152 // retrieve relevant child node
153 // Information is stored on the NT_FILE child node.
154 for (NodeIterator files
= highestAVersion
.getNodes(); files
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
);
166 // if (log.isDebugEnabled()) {
167 // int bundleCount = symbolicNamesToNodes.size();
168 // log.debug("" + bundleCount + " bundles have been indexed for "
173 /** Does the real job : writes JCR META-DATA and generates binaries */
174 protected void internalProcessing(Node groupNode
, JcrMonitor monitor
)
175 throws RepositoryException
{
177 monitor
.subTask("Processing group " + groupId
);
179 Session session
= groupNode
.getSession();
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();
190 throw new SlcException("Group version " + version
193 // int bundleCount = symbolicNamesToNodes.size();
195 // for (Node bundleNode : symbolicNamesToNodes.values()) {
196 // if (log.isDebugEnabled())
197 // log.debug("Processing " + bundleNode.getName() + " ( " + count
198 // + "/" + bundleCount + " )");
200 // // processBundleArtifact(bundleNode);
201 // // bundleNode.getSession().save();
206 Set
<Artifact
> indexes
= new TreeSet
<Artifact
>(
207 new ArtifactIdComparator());
209 Artifact indexArtifact
;
210 indexArtifact
= writeIndex(session
, RepoConstants
.BINARIES_ARTIFACT_ID
,
212 indexes
.add(indexArtifact
);
214 indexArtifact
= writeIndex(session
, RepoConstants
.SOURCES_ARTIFACT_ID
,
216 indexes
.add(indexArtifact
);
219 writeIndex(session
, RepoConstants
.SDK_ARTIFACT_ID
, indexes
);
225 protected void preProcessBundleArtifact(Node bundleNode
)
226 throws RepositoryException
{
228 String symbolicName
= JcrUtils
.get(bundleNode
, SLC_SYMBOLIC_NAME
);
230 if (symbolicName
== null)
231 log
.warn("Symbolic name is null for bundle " + bundleNode
);
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
242 sources
.add(RepoUtils
.asArtifact(bundleNode
));
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);
255 // symbolicNamesToNodes.put(symbolicName, bundleNode);
256 // for (String excludedSuffix : excludedSuffixes) {
257 // if (symbolicName.endsWith(excludedSuffix))
258 // return;// skip adding to binaries
261 binaries
.add(RepoUtils
.asArtifact(bundleNode
));
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.");
269 // protected void processBundleArtifact(Node bundleNode)
270 // throws RepositoryException {
271 // Node artifactFolder = bundleNode.getParent();
272 // String baseName = FilenameUtils.getBaseName(bundleNode.getName());
275 // String pomName = baseName + ".pom";
276 // if (artifactFolder.hasNode(pomName) && !overridePoms)
279 // String pom = generatePomForBundle(bundleNode);
280 // Node pomNode = JcrUtils.copyBytesAsFile(artifactFolder, pomName,
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());
291 // ////////////////////
295 private Artifact
writeIndex(Session session
, String artifactId
,
296 Set
<Artifact
> artifacts
) throws RepositoryException
{
297 Artifact artifact
= new DefaultArtifact(groupId
, artifactId
, "pom",
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
);
308 String pomSha
= JcrUtils
.checksumFile(node
, "SHA-1");
309 JcrUtils
.copyBytesAsFile(node
.getParent(), node
.getName() + ".sha1",
311 String pomMd5
= JcrUtils
.checksumFile(node
, "MD5");
312 JcrUtils
.copyBytesAsFile(node
.getParent(), node
.getName() + ".md5",
319 private Node
getArtifactLatestVersion(Node artifactBase
) {
321 Node highestAVersion
= null;
322 for (NodeIterator aVersions
= artifactBase
.getNodes(); aVersions
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
333 Version cachedHighestVersion
= extractOsgiVersion(allArtifactsHighestVersion
);
334 Version currVersion
= extractOsgiVersion(aVersion
);
335 if (currVersion
.compareTo(cachedHighestVersion
) > 0)
336 allArtifactsHighestVersion
= aVersion
;
339 Version currVersion
= extractOsgiVersion(aVersion
);
340 Version currentHighestVersion
= extractOsgiVersion(highestAVersion
);
341 if (currVersion
.compareTo(currentHighestVersion
) > 0) {
342 highestAVersion
= aVersion
;
345 .compareTo(extractOsgiVersion(allArtifactsHighestVersion
)) > 0) {
346 allArtifactsHighestVersion
= aVersion
;
352 return highestAVersion
;
353 } catch (RepositoryException re
) {
354 throw new SlcException("Unable to get latest version for node "
359 private Version
extractOsgiVersion(Node artifactVersion
)
360 throws RepositoryException
{
361 String rawVersion
= artifactVersion
.getProperty(SLC_ARTIFACT_VERSION
)
363 String cleanVersion
= rawVersion
.replace("-SNAPSHOT", ".SNAPSHOT");
364 Version osgiVersion
= null;
365 // log invalid version value to enable tracking them
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");
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();
388 builder
.append(st
.nextToken()).append(st
.nextToken());
390 builder
.append(st
.nextToken()).append(st
.nextToken());
392 builder
.append(st
.nextToken()).append(st
.nextToken());
394 builder
.append(st
.nextToken());
395 while (st
.hasMoreTokens()) {
398 if (st
.hasMoreTokens())
399 builder
.append("-").append(st
.nextToken());
401 tmpVersion
= builder
.toString();
406 // private String generatePomForBundle(Node n) throws RepositoryException {
407 // String ownSymbolicName = JcrUtils.get(n, SLC_SYMBOLIC_NAME);
409 // StringBuffer p = new StringBuffer();
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>");
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>")
431 // .get(n, SLC_ + Constants.BUNDLE_DESCRIPTION))
432 // .append("</description>\n");
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
445 // if (JcrUtils.check(importPackage, SLC_OPTIONAL))
446 // optionalSymbolicNames.add(dependencySymbolicName);
448 // dependenciesSymbolicNames.add(dependencySymbolicName);
450 // if (!JcrUtils.check(importPackage, SLC_OPTIONAL)
451 // && !systemPackages.contains(pkg))
452 // log.warn("No bundle found for pkg " + pkg);
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);
463 // // TODO require bundles
465 // List<Node> dependencyNodes = new ArrayList<Node>();
466 // for (String depSymbName : dependenciesSymbolicNames) {
467 // if (depSymbName.equals(ownSymbolicName))
468 // continue;// skip self
470 // if (symbolicNamesToNodes.containsKey(depSymbName))
471 // dependencyNodes.add(symbolicNamesToNodes.get(depSymbName));
473 // log.warn("Could not find node for " + depSymbName);
475 // List<Node> optionalDependencyNodes = new ArrayList<Node>();
476 // for (String depSymbName : optionalSymbolicNames) {
477 // if (symbolicNamesToNodes.containsKey(depSymbName))
478 // optionalDependencyNodes.add(symbolicNamesToNodes
479 // .get(depSymbName));
481 // log.warn("Could not find node for " + depSymbName);
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");
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");
509 // p.append("</dependencies>\n");
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");
528 // p.append("</project>\n");
529 // return p.toString();
533 public void setRepository(Repository repository
) {
534 this.repository
= repository
;
537 public void setCredentials(Credentials credentials
) {
538 this.credentials
= credentials
;
541 public void setWorkspace(String workspace
) {
542 this.workspace
= workspace
;
545 public void setGroupId(String groupId
) {
546 this.groupId
= groupId
;
549 public void setParentPomCoordinates(String parentPomCoordinates
) {
550 this.parentPomCoordinates
= parentPomCoordinates
;
553 public void setArtifactBasePath(String artifactBasePath
) {
554 this.artifactBasePath
= artifactBasePath
;
557 public void setVersion(String version
) {
558 this.version
= version
;
561 public void setExcludedSuffixes(List
<String
> excludedSuffixes
) {
562 this.excludedSuffixes
= excludedSuffixes
;
565 public void setArtifactIndexer(ArtifactIndexer artifactIndexer
) {
566 this.artifactIndexer
= artifactIndexer
;