2 * Copyright (C) 2007-2012 Argeo GmbH
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org
.argeo
.slc
.repo
.maven
;
18 import java
.util
.ArrayList
;
19 import java
.util
.List
;
21 import java
.util
.StringTokenizer
;
22 import java
.util
.TreeSet
;
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
;
31 import org
.apache
.commons
.logging
.Log
;
32 import org
.apache
.commons
.logging
.LogFactory
;
33 import org
.argeo
.jcr
.JcrMonitor
;
34 import org
.argeo
.jcr
.JcrUtils
;
35 import org
.argeo
.slc
.SlcException
;
36 import org
.argeo
.slc
.SlcNames
;
37 import org
.argeo
.slc
.SlcTypes
;
38 import org
.argeo
.slc
.repo
.ArtifactIndexer
;
39 import org
.argeo
.slc
.repo
.RepoConstants
;
40 import org
.argeo
.slc
.repo
.RepoUtils
;
41 import org
.eclipse
.aether
.artifact
.Artifact
;
42 import org
.eclipse
.aether
.artifact
.DefaultArtifact
;
43 import org
.osgi
.framework
.Version
;
46 * Generates binaries-, sources- and sdk-version.pom artifacts for a given
49 public class GenerateBinaries
implements Runnable
, SlcNames
{
50 private final static Log log
= LogFactory
.getLog(GenerateBinaries
.class);
53 private Repository repository
;
54 private Credentials credentials
;
55 private String workspace
;
58 private String groupId
;
59 private String parentPomCoordinates
;
60 private String version
= null;
63 private String artifactBasePath
= RepoConstants
.DEFAULT_ARTIFACTS_BASE_PATH
;
64 private List
<String
> excludedSuffixes
= new ArrayList
<String
>();
67 private Set
<Artifact
> binaries
= new TreeSet
<Artifact
>(
68 new ArtifactIdComparator());
69 private Set
<Artifact
> sources
= new TreeSet
<Artifact
>(
70 new ArtifactIdComparator());
73 private ArtifactIndexer artifactIndexer
= new ArtifactIndexer();
74 private Node allArtifactsHighestVersion
;
77 Session session
= null;
79 session
= repository
.login(credentials
, workspace
);
80 Node groupNode
= session
.getNode(MavenConventionsUtils
.groupPath(
81 artifactBasePath
, groupId
));
82 internalPreProcessing(groupNode
, null);
83 internalProcessing(groupNode
, null);
84 } catch (Exception e
) {
85 throw new SlcException("Cannot normalize group " + groupId
+ " in "
88 JcrUtils
.logoutQuietly(session
);
93 * Generates binaries-, sources- and sdk-version.pom artifacts for the given
94 * version (or the highest of all children version if none is precised).
96 * By default, it includes each latest version of all artifact of this
99 * The 3 generated artifacts are then marked as modular distributions and
102 public static void processGroupNode(Node groupNode
, String version
,
103 JcrMonitor monitor
) throws RepositoryException
{
104 // TODO set artifactsBase based on group node
105 GenerateBinaries gb
= new GenerateBinaries();
106 String groupId
= groupNode
.getProperty(SlcNames
.SLC_GROUP_BASE_ID
)
108 gb
.setGroupId(groupId
);
109 gb
.setVersion(version
);
110 // TODO use already done pre-processing
111 gb
.internalPreProcessing(groupNode
, monitor
);
112 gb
.internalProcessing(groupNode
, monitor
);
115 /** Only builds local indexes. Does not change anything in the local Session */
116 public static GenerateBinaries
preProcessGroupNode(Node groupNode
,
117 JcrMonitor monitor
) throws RepositoryException
{
118 // TODO set artifactsBase based on group node
119 GenerateBinaries gb
= new GenerateBinaries();
120 String groupId
= groupNode
.getProperty(SlcNames
.SLC_GROUP_BASE_ID
)
122 gb
.setGroupId(groupId
);
123 // gb.setVersion(version);
124 // gb.setOverridePoms(overridePoms);
125 gb
.internalPreProcessing(groupNode
, monitor
);
129 // exposes indexes. to display results of the pre-processing phase.
130 public Set
<Artifact
> getBinaries() {
134 public Artifact
getHighestArtifactVersion() throws RepositoryException
{
135 return allArtifactsHighestVersion
== null ?
null : RepoUtils
136 .asArtifact(allArtifactsHighestVersion
);
139 // //////////////////////////////////////
143 * Browse all children of a Node considered as a folder that follows Aether
144 * conventions i.e that has Aether's artifact base as children.
146 * Each of such child contains a set of Aether artifact versions. This
147 * methods build the binaries {@code Set<Artifact>} and other indexes. It
148 * does not impact the
150 protected void internalPreProcessing(Node groupNode
, JcrMonitor monitor
)
151 throws RepositoryException
{
153 monitor
.subTask("Pre processing group " + groupId
);
155 // Process all direct children nodes,
156 // gathering latest versions of each artifact
157 allArtifactsHighestVersion
= null;
159 aBases
: for (NodeIterator aBases
= groupNode
.getNodes(); aBases
161 Node aBase
= aBases
.nextNode();
162 if (aBase
.isNodeType(SlcTypes
.SLC_ARTIFACT_BASE
)) {
163 Node highestAVersion
= getArtifactLatestVersion(aBase
);
164 if (highestAVersion
== null)
167 // retrieve relevant child node
168 // Information is stored on the NT_FILE child node.
169 for (NodeIterator files
= highestAVersion
.getNodes(); files
171 Node file
= files
.nextNode();
172 if (file
.isNodeType(SlcTypes
.SLC_BUNDLE_ARTIFACT
)) {
173 if (log
.isDebugEnabled())
174 log
.debug("Pre-Processing " + file
.getName());
175 preProcessBundleArtifact(file
);
181 // if (log.isDebugEnabled()) {
182 // int bundleCount = symbolicNamesToNodes.size();
183 // log.debug("" + bundleCount + " bundles have been indexed for "
188 /** Does the real job : writes JCR META-DATA and generates binaries */
189 protected void internalProcessing(Node groupNode
, JcrMonitor monitor
)
190 throws RepositoryException
{
192 monitor
.subTask("Processing group " + groupId
);
194 Session session
= groupNode
.getSession();
196 // if version not set or empty, use the highest version
197 // useful when indexing a product maven repository where
198 // all artifacts have the same version for a given release
199 // => the version can then be left empty
200 if (version
== null || version
.trim().equals(""))
201 if (allArtifactsHighestVersion
!= null)
202 version
= allArtifactsHighestVersion
.getProperty(
203 SLC_ARTIFACT_VERSION
).getString();
205 throw new SlcException("Group version " + version
208 // int bundleCount = symbolicNamesToNodes.size();
210 // for (Node bundleNode : symbolicNamesToNodes.values()) {
211 // if (log.isDebugEnabled())
212 // log.debug("Processing " + bundleNode.getName() + " ( " + count
213 // + "/" + bundleCount + " )");
215 // // processBundleArtifact(bundleNode);
216 // // bundleNode.getSession().save();
221 Set
<Artifact
> indexes
= new TreeSet
<Artifact
>(
222 new ArtifactIdComparator());
224 Artifact indexArtifact
;
225 indexArtifact
= writeIndex(session
, RepoConstants
.BINARIES_ARTIFACT_ID
,
227 indexes
.add(indexArtifact
);
229 indexArtifact
= writeIndex(session
, RepoConstants
.SOURCES_ARTIFACT_ID
,
231 indexes
.add(indexArtifact
);
234 writeIndex(session
, RepoConstants
.SDK_ARTIFACT_ID
, indexes
);
240 protected void preProcessBundleArtifact(Node bundleNode
)
241 throws RepositoryException
{
243 String symbolicName
= JcrUtils
.get(bundleNode
, SLC_SYMBOLIC_NAME
);
245 if (symbolicName
== null)
246 log
.warn("Symbolic name is null for bundle " + bundleNode
);
248 // Manage source bundles
249 if (symbolicName
.endsWith(".source")) {
250 // TODO make a shared node with classifier 'sources'?
251 String bundleName
= RepoUtils
252 .extractBundleNameFromSourceName(symbolicName
);
253 for (String excludedSuffix
: excludedSuffixes
) {
254 if (bundleName
.endsWith(excludedSuffix
))
255 return;// skip adding to sources
257 sources
.add(RepoUtils
.asArtifact(bundleNode
));
262 // NodeIterator exportPackages = bundleNode.getNodes(SLC_
263 // + Constants.EXPORT_PACKAGE);
264 // while (exportPackages.hasNext()) {
265 // Node exportPackage = exportPackages.nextNode();
266 // String pkg = JcrUtils.get(exportPackage, SLC_NAME);
267 // packagesToSymbolicNames.put(pkg, symbolicName);
270 // symbolicNamesToNodes.put(symbolicName, bundleNode);
271 // for (String excludedSuffix : excludedSuffixes) {
272 // if (symbolicName.endsWith(excludedSuffix))
273 // return;// skip adding to binaries
276 binaries
.add(RepoUtils
.asArtifact(bundleNode
));
278 // Extra check. to remove
279 if (bundleNode
.getSession().hasPendingChanges())
280 throw new SlcException("Pending changes in the session, "
281 + "this should not be true here.");
284 // protected void processBundleArtifact(Node bundleNode)
285 // throws RepositoryException {
286 // Node artifactFolder = bundleNode.getParent();
287 // String baseName = FilenameUtils.getBaseName(bundleNode.getName());
290 // String pomName = baseName + ".pom";
291 // if (artifactFolder.hasNode(pomName) && !overridePoms)
294 // String pom = generatePomForBundle(bundleNode);
295 // Node pomNode = JcrUtils.copyBytesAsFile(artifactFolder, pomName,
298 // String bundleSha = JcrUtils.checksumFile(bundleNode, "SHA-1");
299 // JcrUtils.copyBytesAsFile(artifactFolder,
300 // bundleNode.getName() + ".sha1", bundleSha.getBytes());
301 // String pomSha = JcrUtils.checksumFile(pomNode, "SHA-1");
302 // JcrUtils.copyBytesAsFile(artifactFolder, pomNode.getName() + ".sha1",
303 // pomSha.getBytes());
306 // ////////////////////
310 private Artifact
writeIndex(Session session
, String artifactId
,
311 Set
<Artifact
> artifacts
) throws RepositoryException
{
312 Artifact artifact
= new DefaultArtifact(groupId
, artifactId
, "pom",
314 Artifact parentArtifact
= parentPomCoordinates
!= null ?
new DefaultArtifact(
315 parentPomCoordinates
) : null;
316 String pom
= MavenConventionsUtils
.artifactsAsDependencyPom(artifact
,
317 artifacts
, parentArtifact
);
318 Node node
= RepoUtils
.copyBytesAsArtifact(
319 session
.getNode(artifactBasePath
), artifact
, pom
.getBytes());
320 artifactIndexer
.index(node
);
323 String pomSha
= JcrUtils
.checksumFile(node
, "SHA-1");
324 JcrUtils
.copyBytesAsFile(node
.getParent(), node
.getName() + ".sha1",
326 String pomMd5
= JcrUtils
.checksumFile(node
, "MD5");
327 JcrUtils
.copyBytesAsFile(node
.getParent(), node
.getName() + ".md5",
334 private Node
getArtifactLatestVersion(Node artifactBase
) {
336 Node highestAVersion
= null;
337 for (NodeIterator aVersions
= artifactBase
.getNodes(); aVersions
339 Node aVersion
= aVersions
.nextNode();
340 if (aVersion
.isNodeType(SlcTypes
.SLC_ARTIFACT_VERSION_BASE
)) {
341 if (highestAVersion
== null) {
342 highestAVersion
= aVersion
;
343 if (allArtifactsHighestVersion
== null)
344 allArtifactsHighestVersion
= aVersion
;
345 // Correctly handle following arrival order:
346 // Name1 - V1, name2 - V3
348 Version cachedHighestVersion
= extractOsgiVersion(allArtifactsHighestVersion
);
349 Version currVersion
= extractOsgiVersion(aVersion
);
350 if (currVersion
.compareTo(cachedHighestVersion
) > 0)
351 allArtifactsHighestVersion
= aVersion
;
354 Version currVersion
= extractOsgiVersion(aVersion
);
355 Version currentHighestVersion
= extractOsgiVersion(highestAVersion
);
356 if (currVersion
.compareTo(currentHighestVersion
) > 0) {
357 highestAVersion
= aVersion
;
360 .compareTo(extractOsgiVersion(allArtifactsHighestVersion
)) > 0) {
361 allArtifactsHighestVersion
= aVersion
;
367 return highestAVersion
;
368 } catch (RepositoryException re
) {
369 throw new SlcException("Unable to get latest version for node "
374 private Version
extractOsgiVersion(Node artifactVersion
)
375 throws RepositoryException
{
376 String rawVersion
= artifactVersion
.getProperty(SLC_ARTIFACT_VERSION
)
378 String cleanVersion
= rawVersion
.replace("-SNAPSHOT", ".SNAPSHOT");
379 Version osgiVersion
= null;
380 // log invalid version value to enable tracking them
382 osgiVersion
= new Version(cleanVersion
);
383 } catch (IllegalArgumentException e
) {
384 log
.error("Version string " + cleanVersion
+ " is invalid ");
385 String twickedVersion
= twickInvalidVersion(cleanVersion
);
386 osgiVersion
= new Version(twickedVersion
);
387 log
.error("Using " + twickedVersion
+ " instead");
393 private String
twickInvalidVersion(String tmpVersion
) {
394 String
[] tokens
= tmpVersion
.split("\\.");
395 if (tokens
.length
== 3 && tokens
[2].lastIndexOf("-") > 0) {
396 String newSuffix
= tokens
[2].replaceFirst("-", ".");
397 tmpVersion
= tmpVersion
.replaceFirst(tokens
[2], newSuffix
);
398 } else if (tokens
.length
> 4) {
399 // FIXME manually remove other "."
400 StringTokenizer st
= new StringTokenizer(tmpVersion
, ".", true);
401 StringBuilder builder
= new StringBuilder();
403 builder
.append(st
.nextToken()).append(st
.nextToken());
405 builder
.append(st
.nextToken()).append(st
.nextToken());
407 builder
.append(st
.nextToken()).append(st
.nextToken());
409 builder
.append(st
.nextToken());
410 while (st
.hasMoreTokens()) {
413 if (st
.hasMoreTokens())
414 builder
.append("-").append(st
.nextToken());
416 tmpVersion
= builder
.toString();
421 // private String generatePomForBundle(Node n) throws RepositoryException {
422 // String ownSymbolicName = JcrUtils.get(n, SLC_SYMBOLIC_NAME);
424 // StringBuffer p = new StringBuffer();
427 // p.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
428 // 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");
429 // p.append("<modelVersion>4.0.0</modelVersion>");
432 // p.append("<groupId>").append(JcrUtils.get(n, SLC_GROUP_ID))
433 // .append("</groupId>\n");
434 // p.append("<artifactId>").append(JcrUtils.get(n, SLC_ARTIFACT_ID))
435 // .append("</artifactId>\n");
436 // p.append("<version>").append(JcrUtils.get(n, SLC_ARTIFACT_VERSION))
437 // .append("</version>\n");
438 // p.append("<packaging>pom</packaging>\n");
439 // if (n.hasProperty(SLC_ + Constants.BUNDLE_NAME))
440 // p.append("<name>")
441 // .append(JcrUtils.get(n, SLC_ + Constants.BUNDLE_NAME))
442 // .append("</name>\n");
443 // if (n.hasProperty(SLC_ + Constants.BUNDLE_DESCRIPTION))
444 // p.append("<description>")
446 // .get(n, SLC_ + Constants.BUNDLE_DESCRIPTION))
447 // .append("</description>\n");
450 // Set<String> dependenciesSymbolicNames = new TreeSet<String>();
451 // Set<String> optionalSymbolicNames = new TreeSet<String>();
452 // NodeIterator importPackages = n.getNodes(SLC_
453 // + Constants.IMPORT_PACKAGE);
454 // while (importPackages.hasNext()) {
455 // Node importPackage = importPackages.nextNode();
456 // String pkg = JcrUtils.get(importPackage, SLC_NAME);
457 // if (packagesToSymbolicNames.containsKey(pkg)) {
458 // String dependencySymbolicName = packagesToSymbolicNames
460 // if (JcrUtils.check(importPackage, SLC_OPTIONAL))
461 // optionalSymbolicNames.add(dependencySymbolicName);
463 // dependenciesSymbolicNames.add(dependencySymbolicName);
465 // if (!JcrUtils.check(importPackage, SLC_OPTIONAL)
466 // && !systemPackages.contains(pkg))
467 // log.warn("No bundle found for pkg " + pkg);
471 // if (n.hasNode(SLC_ + Constants.FRAGMENT_HOST)) {
472 // String fragmentHost = JcrUtils.get(
473 // n.getNode(SLC_ + Constants.FRAGMENT_HOST),
474 // SLC_SYMBOLIC_NAME);
475 // dependenciesSymbolicNames.add(fragmentHost);
478 // // TODO require bundles
480 // List<Node> dependencyNodes = new ArrayList<Node>();
481 // for (String depSymbName : dependenciesSymbolicNames) {
482 // if (depSymbName.equals(ownSymbolicName))
483 // continue;// skip self
485 // if (symbolicNamesToNodes.containsKey(depSymbName))
486 // dependencyNodes.add(symbolicNamesToNodes.get(depSymbName));
488 // log.warn("Could not find node for " + depSymbName);
490 // List<Node> optionalDependencyNodes = new ArrayList<Node>();
491 // for (String depSymbName : optionalSymbolicNames) {
492 // if (symbolicNamesToNodes.containsKey(depSymbName))
493 // optionalDependencyNodes.add(symbolicNamesToNodes
494 // .get(depSymbName));
496 // log.warn("Could not find node for " + depSymbName);
499 // p.append("<dependencies>\n");
500 // for (Node dependencyNode : dependencyNodes) {
501 // p.append("<dependency>\n");
502 // p.append("\t<groupId>")
503 // .append(JcrUtils.get(dependencyNode, SLC_GROUP_ID))
504 // .append("</groupId>\n");
505 // p.append("\t<artifactId>")
506 // .append(JcrUtils.get(dependencyNode, SLC_ARTIFACT_ID))
507 // .append("</artifactId>\n");
508 // p.append("</dependency>\n");
511 // if (optionalDependencyNodes.size() > 0)
512 // p.append("<!-- OPTIONAL -->\n");
513 // for (Node dependencyNode : optionalDependencyNodes) {
514 // p.append("<dependency>\n");
515 // p.append("\t<groupId>")
516 // .append(JcrUtils.get(dependencyNode, SLC_GROUP_ID))
517 // .append("</groupId>\n");
518 // p.append("\t<artifactId>")
519 // .append(JcrUtils.get(dependencyNode, SLC_ARTIFACT_ID))
520 // .append("</artifactId>\n");
521 // p.append("\t<optional>true</optional>\n");
522 // p.append("</dependency>\n");
524 // p.append("</dependencies>\n");
526 // // Dependency management
527 // p.append("<dependencyManagement>\n");
528 // p.append("<dependencies>\n");
529 // p.append("<dependency>\n");
530 // p.append("\t<groupId>").append(groupId).append("</groupId>\n");
531 // p.append("\t<artifactId>")
532 // .append(ownSymbolicName.endsWith(".source") ?
533 // RepoConstants.SOURCES_ARTIFACT_ID
534 // : RepoConstants.BINARIES_ARTIFACT_ID)
535 // .append("</artifactId>\n");
536 // p.append("\t<version>").append(version).append("</version>\n");
537 // p.append("\t<type>pom</type>\n");
538 // p.append("\t<scope>import</scope>\n");
539 // p.append("</dependency>\n");
540 // p.append("</dependencies>\n");
541 // p.append("</dependencyManagement>\n");
543 // p.append("</project>\n");
544 // return p.toString();
548 public void setRepository(Repository repository
) {
549 this.repository
= repository
;
552 public void setCredentials(Credentials credentials
) {
553 this.credentials
= credentials
;
556 public void setWorkspace(String workspace
) {
557 this.workspace
= workspace
;
560 public void setGroupId(String groupId
) {
561 this.groupId
= groupId
;
564 public void setParentPomCoordinates(String parentPomCoordinates
) {
565 this.parentPomCoordinates
= parentPomCoordinates
;
568 public void setArtifactBasePath(String artifactBasePath
) {
569 this.artifactBasePath
= artifactBasePath
;
572 public void setVersion(String version
) {
573 this.version
= version
;
576 public void setExcludedSuffixes(List
<String
> excludedSuffixes
) {
577 this.excludedSuffixes
= excludedSuffixes
;
580 public void setArtifactIndexer(ArtifactIndexer artifactIndexer
) {
581 this.artifactIndexer
= artifactIndexer
;