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
.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
;
47 * Generates binaries-, sources- and sdk-version.pom artifacts for a given
50 public class GenerateBinaries
implements Runnable
, SlcNames
{
51 private final static Log log
= LogFactory
.getLog(GenerateBinaries
.class);
54 private Repository repository
;
55 private Credentials credentials
;
56 private String workspace
;
59 private String groupId
;
60 private String parentPomCoordinates
;
61 private String version
= null;
64 private String artifactBasePath
= RepoConstants
.DEFAULT_ARTIFACTS_BASE_PATH
;
65 private List
<String
> excludedSuffixes
= new ArrayList
<String
>();
68 private Set
<Artifact
> binaries
= new TreeSet
<Artifact
>(
69 new ArtifactIdComparator());
70 private Set
<Artifact
> sources
= new TreeSet
<Artifact
>(
71 new ArtifactIdComparator());
74 private ArtifactIndexer artifactIndexer
= new ArtifactIndexer();
75 private Node allArtifactsHighestVersion
;
78 Session session
= null;
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 "
89 JcrUtils
.logoutQuietly(session
);
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).
97 * By default, it includes each latest version of all artifact of this
100 * The 3 generated artifacts are then marked as modular distributions and
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
)
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
);
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
)
123 gb
.setGroupId(groupId
);
124 // gb.setVersion(version);
125 // gb.setOverridePoms(overridePoms);
126 gb
.internalPreProcessing(groupNode
, monitor
);
130 // exposes indexes. to display results of the pre-processing phase.
131 public Set
<Artifact
> getBinaries() {
135 public Artifact
getHighestArtifactVersion() throws RepositoryException
{
136 return allArtifactsHighestVersion
== null ?
null : RepoUtils
137 .asArtifact(allArtifactsHighestVersion
);
140 // //////////////////////////////////////
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.
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
151 protected void internalPreProcessing(Node groupNode
, ArgeoMonitor monitor
)
152 throws RepositoryException
{
154 monitor
.subTask("Pre processing group " + groupId
);
156 // Process all direct children nodes,
157 // gathering latest versions of each artifact
158 allArtifactsHighestVersion
= null;
160 aBases
: for (NodeIterator aBases
= groupNode
.getNodes(); aBases
162 Node aBase
= aBases
.nextNode();
163 if (aBase
.isNodeType(SlcTypes
.SLC_ARTIFACT_BASE
)) {
164 Node highestAVersion
= getArtifactLatestVersion(aBase
);
165 if (highestAVersion
== null)
168 // retrieve relevant child node
169 // Information is stored on the NT_FILE child node.
170 for (NodeIterator files
= highestAVersion
.getNodes(); files
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
);
182 // if (log.isDebugEnabled()) {
183 // int bundleCount = symbolicNamesToNodes.size();
184 // log.debug("" + bundleCount + " bundles have been indexed for "
189 /** Does the real job : writes JCR META-DATA and generates binaries */
190 protected void internalProcessing(Node groupNode
, ArgeoMonitor monitor
)
191 throws RepositoryException
{
193 monitor
.subTask("Processing group " + groupId
);
195 Session session
= groupNode
.getSession();
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();
206 throw new SlcException("Group version " + version
209 // int bundleCount = symbolicNamesToNodes.size();
211 // for (Node bundleNode : symbolicNamesToNodes.values()) {
212 // if (log.isDebugEnabled())
213 // log.debug("Processing " + bundleNode.getName() + " ( " + count
214 // + "/" + bundleCount + " )");
216 // // processBundleArtifact(bundleNode);
217 // // bundleNode.getSession().save();
222 Set
<Artifact
> indexes
= new TreeSet
<Artifact
>(
223 new ArtifactIdComparator());
225 Artifact indexArtifact
;
226 indexArtifact
= writeIndex(session
, RepoConstants
.BINARIES_ARTIFACT_ID
,
228 indexes
.add(indexArtifact
);
230 indexArtifact
= writeIndex(session
, RepoConstants
.SOURCES_ARTIFACT_ID
,
232 indexes
.add(indexArtifact
);
235 writeIndex(session
, RepoConstants
.SDK_ARTIFACT_ID
, indexes
);
241 protected void preProcessBundleArtifact(Node bundleNode
)
242 throws RepositoryException
{
244 String symbolicName
= JcrUtils
.get(bundleNode
, SLC_SYMBOLIC_NAME
);
246 if (symbolicName
== null)
247 log
.warn("Symbolic name is null for bundle " + bundleNode
);
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
258 sources
.add(RepoUtils
.asArtifact(bundleNode
));
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);
271 // symbolicNamesToNodes.put(symbolicName, bundleNode);
272 // for (String excludedSuffix : excludedSuffixes) {
273 // if (symbolicName.endsWith(excludedSuffix))
274 // return;// skip adding to binaries
277 binaries
.add(RepoUtils
.asArtifact(bundleNode
));
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.");
285 // protected void processBundleArtifact(Node bundleNode)
286 // throws RepositoryException {
287 // Node artifactFolder = bundleNode.getParent();
288 // String baseName = FilenameUtils.getBaseName(bundleNode.getName());
291 // String pomName = baseName + ".pom";
292 // if (artifactFolder.hasNode(pomName) && !overridePoms)
295 // String pom = generatePomForBundle(bundleNode);
296 // Node pomNode = JcrUtils.copyBytesAsFile(artifactFolder, pomName,
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());
307 // ////////////////////
311 private Artifact
writeIndex(Session session
, String artifactId
,
312 Set
<Artifact
> artifacts
) throws RepositoryException
{
313 Artifact artifact
= new DefaultArtifact(groupId
, artifactId
, "pom",
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
);
324 String pomSha
= JcrUtils
.checksumFile(node
, "SHA-1");
325 JcrUtils
.copyBytesAsFile(node
.getParent(), node
.getName() + ".sha1",
327 String pomMd5
= JcrUtils
.checksumFile(node
, "MD5");
328 JcrUtils
.copyBytesAsFile(node
.getParent(), node
.getName() + ".md5",
335 private Node
getArtifactLatestVersion(Node artifactBase
) {
337 Node highestAVersion
= null;
338 for (NodeIterator aVersions
= artifactBase
.getNodes(); aVersions
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
349 Version cachedHighestVersion
= extractOsgiVersion(allArtifactsHighestVersion
);
350 Version currVersion
= extractOsgiVersion(aVersion
);
351 if (currVersion
.compareTo(cachedHighestVersion
) > 0)
352 allArtifactsHighestVersion
= aVersion
;
355 Version currVersion
= extractOsgiVersion(aVersion
);
356 Version currentHighestVersion
= extractOsgiVersion(highestAVersion
);
357 if (currVersion
.compareTo(currentHighestVersion
) > 0) {
358 highestAVersion
= aVersion
;
361 .compareTo(extractOsgiVersion(allArtifactsHighestVersion
)) > 0) {
362 allArtifactsHighestVersion
= aVersion
;
368 return highestAVersion
;
369 } catch (RepositoryException re
) {
370 throw new SlcException("Unable to get latest version for node "
375 private Version
extractOsgiVersion(Node artifactVersion
)
376 throws RepositoryException
{
377 String rawVersion
= artifactVersion
.getProperty(SLC_ARTIFACT_VERSION
)
379 String cleanVersion
= rawVersion
.replace("-SNAPSHOT", ".SNAPSHOT");
380 Version osgiVersion
= null;
381 // log invalid version value to enable tracking them
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");
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();
404 builder
.append(st
.nextToken()).append(st
.nextToken());
406 builder
.append(st
.nextToken()).append(st
.nextToken());
408 builder
.append(st
.nextToken()).append(st
.nextToken());
410 builder
.append(st
.nextToken());
411 while (st
.hasMoreTokens()) {
414 if (st
.hasMoreTokens())
415 builder
.append("-").append(st
.nextToken());
417 tmpVersion
= builder
.toString();
422 // private String generatePomForBundle(Node n) throws RepositoryException {
423 // String ownSymbolicName = JcrUtils.get(n, SLC_SYMBOLIC_NAME);
425 // StringBuffer p = new StringBuffer();
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>");
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>")
447 // .get(n, SLC_ + Constants.BUNDLE_DESCRIPTION))
448 // .append("</description>\n");
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
461 // if (JcrUtils.check(importPackage, SLC_OPTIONAL))
462 // optionalSymbolicNames.add(dependencySymbolicName);
464 // dependenciesSymbolicNames.add(dependencySymbolicName);
466 // if (!JcrUtils.check(importPackage, SLC_OPTIONAL)
467 // && !systemPackages.contains(pkg))
468 // log.warn("No bundle found for pkg " + pkg);
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);
479 // // TODO require bundles
481 // List<Node> dependencyNodes = new ArrayList<Node>();
482 // for (String depSymbName : dependenciesSymbolicNames) {
483 // if (depSymbName.equals(ownSymbolicName))
484 // continue;// skip self
486 // if (symbolicNamesToNodes.containsKey(depSymbName))
487 // dependencyNodes.add(symbolicNamesToNodes.get(depSymbName));
489 // log.warn("Could not find node for " + depSymbName);
491 // List<Node> optionalDependencyNodes = new ArrayList<Node>();
492 // for (String depSymbName : optionalSymbolicNames) {
493 // if (symbolicNamesToNodes.containsKey(depSymbName))
494 // optionalDependencyNodes.add(symbolicNamesToNodes
495 // .get(depSymbName));
497 // log.warn("Could not find node for " + depSymbName);
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");
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");
525 // p.append("</dependencies>\n");
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");
544 // p.append("</project>\n");
545 // return p.toString();
549 public void setRepository(Repository repository
) {
550 this.repository
= repository
;
553 public void setCredentials(Credentials credentials
) {
554 this.credentials
= credentials
;
557 public void setWorkspace(String workspace
) {
558 this.workspace
= workspace
;
561 public void setGroupId(String groupId
) {
562 this.groupId
= groupId
;
565 public void setParentPomCoordinates(String parentPomCoordinates
) {
566 this.parentPomCoordinates
= parentPomCoordinates
;
569 public void setArtifactBasePath(String artifactBasePath
) {
570 this.artifactBasePath
= artifactBasePath
;
573 public void setVersion(String version
) {
574 this.version
= version
;
577 public void setExcludedSuffixes(List
<String
> excludedSuffixes
) {
578 this.excludedSuffixes
= excludedSuffixes
;
581 public void setArtifactIndexer(ArtifactIndexer artifactIndexer
) {
582 this.artifactIndexer
= artifactIndexer
;