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
.HashMap
;
20 import java
.util
.List
;
23 import java
.util
.TreeSet
;
25 import javax
.jcr
.Credentials
;
26 import javax
.jcr
.Node
;
27 import javax
.jcr
.NodeIterator
;
28 import javax
.jcr
.Repository
;
29 import javax
.jcr
.RepositoryException
;
30 import javax
.jcr
.Session
;
32 import org
.apache
.commons
.io
.FilenameUtils
;
33 import org
.apache
.commons
.logging
.Log
;
34 import org
.apache
.commons
.logging
.LogFactory
;
35 import org
.argeo
.ArgeoMonitor
;
36 import org
.argeo
.jcr
.JcrUtils
;
37 import org
.argeo
.slc
.SlcException
;
38 import org
.argeo
.slc
.aether
.ArtifactIdComparator
;
39 import org
.argeo
.slc
.jcr
.SlcNames
;
40 import org
.argeo
.slc
.jcr
.SlcTypes
;
41 import org
.argeo
.slc
.repo
.ArtifactIndexer
;
42 import org
.argeo
.slc
.repo
.RepoConstants
;
43 import org
.argeo
.slc
.repo
.RepoUtils
;
44 import org
.argeo
.slc
.repo
.osgi
.OsgiProfile
;
45 import org
.osgi
.framework
.Constants
;
46 import org
.osgi
.framework
.Version
;
47 import org
.sonatype
.aether
.artifact
.Artifact
;
48 import org
.sonatype
.aether
.util
.artifact
.DefaultArtifact
;
51 * Make sure that all JCR metadata and Maven metadata are consistent for this
52 * group of OSGi bundles.
54 public class GenerateBinaries
implements Runnable
, SlcNames
{
55 private final static Log log
= LogFactory
.getLog(GenerateBinaries
.class);
58 private Repository repository
;
59 private Credentials credentials
;
60 private String workspace
;
63 private String groupId
;
64 private String parentPomCoordinates
;
65 private Boolean overridePoms
= false;
66 private String version
= null;
69 private String artifactBasePath
= RepoConstants
.DEFAULT_ARTIFACTS_BASE_PATH
;
70 private List
<String
> excludedSuffixes
= new ArrayList
<String
>();
71 /** TODO make it more generic */
72 private List
<String
> systemPackages
= OsgiProfile
.PROFILE_JAVA_SE_1_6
75 private ArtifactIndexer artifactIndexer
= new ArtifactIndexer();
78 private Map
<String
, String
> packagesToSymbolicNames
= new HashMap
<String
, String
>();
79 private Map
<String
, Node
> symbolicNamesToNodes
= new HashMap
<String
, Node
>();
80 private Set
<Artifact
> binaries
= new TreeSet
<Artifact
>(
81 new ArtifactIdComparator());
82 private Set
<Artifact
> sources
= new TreeSet
<Artifact
>(
83 new ArtifactIdComparator());
84 private Node allArtifactsHighestVersion
;
87 Session session
= null;
89 session
= repository
.login(workspace
);
90 Node groupNode
= session
.getNode(MavenConventionsUtils
.groupPath(
91 artifactBasePath
, groupId
));
92 internalPreProcessing(groupNode
, null);
93 processGroupNode(groupNode
, null);
94 } catch (Exception e
) {
95 throw new SlcException("Cannot normalize group " + groupId
+ " in "
98 JcrUtils
.logoutQuietly(session
);
102 public static void processGroupNode(Node groupNode
, String version
,
103 Boolean overridePoms
, ArgeoMonitor monitor
)
104 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 gb
.setOverridePoms(overridePoms
);
112 // TODO use already done pre-processing
113 gb
.internalPreProcessing(groupNode
, monitor
);
114 gb
.processGroupNode(groupNode
, monitor
);
117 /** Only builds local indexes. Does not change anything in the local Session */
118 public static GenerateBinaries
preProcessGroupNode(Node groupNode
,
119 ArgeoMonitor monitor
) throws RepositoryException
{
120 // TODO set artifactsBase based on group node
121 GenerateBinaries gb
= new GenerateBinaries();
122 String groupId
= groupNode
.getProperty(SlcNames
.SLC_GROUP_BASE_ID
)
124 gb
.setGroupId(groupId
);
125 // gb.setVersion(version);
126 // gb.setOverridePoms(overridePoms);
127 gb
.internalPreProcessing(groupNode
, monitor
);
131 // exposes indexes. to display results of the pre-processing phase.
132 public Set
<Artifact
> getBinaries() {
136 public Artifact
getHighestArtifactVersion() throws RepositoryException
{
137 return allArtifactsHighestVersion
== null ?
null : RepoUtils
138 .asArtifact(allArtifactsHighestVersion
);
141 protected void internalPreProcessing(Node groupNode
, ArgeoMonitor monitor
)
142 throws RepositoryException
{
144 monitor
.subTask("Pre processing group " + groupId
);
146 // Process all direct children nodes,
147 // gathering latest versions of all artifact base
148 allArtifactsHighestVersion
= null;
149 // Session session = groupNode.getSession();
150 aBases
: for (NodeIterator aBases
= groupNode
.getNodes(); aBases
152 Node aBase
= aBases
.nextNode();
153 if (aBase
.isNodeType(SlcTypes
.SLC_ARTIFACT_BASE
)) {
154 Node highestAVersion
= getArtifactLatestVersion(aBase
);
155 if (highestAVersion
== null)
158 // retrieve relevant child node
159 for (NodeIterator files
= highestAVersion
.getNodes(); files
161 Node file
= files
.nextNode();
162 if (file
.isNodeType(SlcTypes
.SLC_BUNDLE_ARTIFACT
)) {
163 preProcessBundleArtifact(file
);
164 if (log
.isDebugEnabled())
165 log
.debug("Pre-processed " + file
.getName());
171 if (log
.isDebugEnabled()) {
172 int bundleCount
= symbolicNamesToNodes
.size();
173 log
.debug("" + bundleCount
+ " bundles have been indexed for "
178 /** Does the real job : writes JCR META-DATA and generates binaries */
179 protected void processGroupNode(Node groupNode
, ArgeoMonitor monitor
)
180 throws RepositoryException
{
182 monitor
.subTask("Processing group " + groupId
);
184 Session session
= groupNode
.getSession();
186 // if version not set or empty, use the highest version
187 // useful when indexing a product maven repository where
188 // all artifacts have the same version for a given release
189 // => the version can then be left empty
190 if (version
== null || version
.trim().equals(""))
191 if (allArtifactsHighestVersion
!= null)
192 version
= allArtifactsHighestVersion
.getProperty(
193 SLC_ARTIFACT_VERSION
).getString();
195 throw new SlcException("Group version " + version
198 int bundleCount
= symbolicNamesToNodes
.size();
201 for (Node bundleNode
: symbolicNamesToNodes
.values()) {
202 processBundleArtifact(bundleNode
);
203 bundleNode
.getSession().save();
204 if (log
.isDebugEnabled())
205 log
.debug(count
+ "/" + bundleCount
+ " Processed "
206 + bundleNode
.getName());
211 Set
<Artifact
> indexes
= new TreeSet
<Artifact
>(
212 new ArtifactIdComparator());
213 Artifact indexArtifact
= writeIndex(session
,
214 RepoConstants
.BINARIES_ARTIFACT_ID
, binaries
);
215 indexes
.add(indexArtifact
);
216 indexArtifact
= writeIndex(session
, RepoConstants
.SOURCES_ARTIFACT_ID
,
218 indexes
.add(indexArtifact
);
220 writeIndex(session
, RepoConstants
.SDK_ARTIFACT_ID
, indexes
);
226 private Node
getArtifactLatestVersion(Node artifactBase
) {
228 Node highestAVersion
= null;
229 for (NodeIterator aVersions
= artifactBase
.getNodes(); aVersions
231 Node aVersion
= aVersions
.nextNode();
232 if (aVersion
.isNodeType(SlcTypes
.SLC_ARTIFACT_VERSION_BASE
)) {
233 if (highestAVersion
== null) {
234 highestAVersion
= aVersion
;
235 if (allArtifactsHighestVersion
== null)
236 allArtifactsHighestVersion
= aVersion
;
237 // Correctly handle following arrival order:
238 // Name1 - V1, name2 - V3
240 Version cachedHighestVersion
= extractOsgiVersion(allArtifactsHighestVersion
);
241 Version currVersion
= extractOsgiVersion(aVersion
);
242 if (currVersion
.compareTo(cachedHighestVersion
) > 0)
243 allArtifactsHighestVersion
= aVersion
;
246 Version currVersion
= extractOsgiVersion(aVersion
);
247 Version currentHighestVersion
= extractOsgiVersion(highestAVersion
);
248 if (currVersion
.compareTo(currentHighestVersion
) > 0) {
249 highestAVersion
= aVersion
;
252 .compareTo(extractOsgiVersion(allArtifactsHighestVersion
)) > 0) {
253 allArtifactsHighestVersion
= aVersion
;
259 return highestAVersion
;
260 } catch (RepositoryException re
) {
261 throw new SlcException("Unable to get latest version for node "
266 private Version
extractOsgiVersion(Node artifactVersion
)
267 throws RepositoryException
{
268 String rawVersion
= artifactVersion
.getProperty(SLC_ARTIFACT_VERSION
)
270 String cleanVersion
= rawVersion
.replace("-SNAPSHOT", ".SNAPSHOT");
271 return new Version(cleanVersion
);
274 protected void preProcessBundleArtifact(Node bundleNode
)
275 throws RepositoryException
{
277 String symbolicName
= JcrUtils
.get(bundleNode
, SLC_SYMBOLIC_NAME
);
278 if (symbolicName
.endsWith(".source")) {
279 // TODO make a shared node with classifier 'sources'?
280 String bundleName
= RepoUtils
281 .extractBundleNameFromSourceName(symbolicName
);
282 for (String excludedSuffix
: excludedSuffixes
) {
283 if (bundleName
.endsWith(excludedSuffix
))
284 return;// skip adding to sources
286 sources
.add(RepoUtils
.asArtifact(bundleNode
));
290 NodeIterator exportPackages
= bundleNode
.getNodes(SLC_
291 + Constants
.EXPORT_PACKAGE
);
292 while (exportPackages
.hasNext()) {
293 Node exportPackage
= exportPackages
.nextNode();
294 String pkg
= JcrUtils
.get(exportPackage
, SLC_NAME
);
295 packagesToSymbolicNames
.put(pkg
, symbolicName
);
298 symbolicNamesToNodes
.put(symbolicName
, bundleNode
);
299 for (String excludedSuffix
: excludedSuffixes
) {
300 if (symbolicName
.endsWith(excludedSuffix
))
301 return;// skip adding to binaries
303 binaries
.add(RepoUtils
.asArtifact(bundleNode
));
305 if (bundleNode
.getSession().hasPendingChanges())
306 throw new SlcException("Pending changes in the session, "
307 + "this should not be true here.");
308 // bundleNode.getSession().save();
311 protected void processBundleArtifact(Node bundleNode
)
312 throws RepositoryException
{
313 Node artifactFolder
= bundleNode
.getParent();
314 String baseName
= FilenameUtils
.getBaseName(bundleNode
.getName());
317 String pomName
= baseName
+ ".pom";
318 if (artifactFolder
.hasNode(pomName
) && !overridePoms
)
321 String pom
= generatePomForBundle(bundleNode
);
322 Node pomNode
= JcrUtils
.copyBytesAsFile(artifactFolder
, pomName
,
325 String bundleSha
= JcrUtils
.checksumFile(bundleNode
, "SHA-1");
326 JcrUtils
.copyBytesAsFile(artifactFolder
,
327 bundleNode
.getName() + ".sha1", bundleSha
.getBytes());
328 String pomSha
= JcrUtils
.checksumFile(pomNode
, "SHA-1");
329 JcrUtils
.copyBytesAsFile(artifactFolder
, pomNode
.getName() + ".sha1",
334 private Artifact
writeIndex(Session session
, String artifactId
,
335 Set
<Artifact
> artifacts
) throws RepositoryException
{
336 Artifact artifact
= new DefaultArtifact(groupId
, artifactId
, "pom",
338 Artifact parentArtifact
= parentPomCoordinates
!= null ?
new DefaultArtifact(
339 parentPomCoordinates
) : null;
340 String pom
= MavenConventionsUtils
.artifactsAsDependencyPom(artifact
,
341 artifacts
, parentArtifact
);
342 Node node
= RepoUtils
.copyBytesAsArtifact(
343 session
.getNode(artifactBasePath
), artifact
, pom
.getBytes());
344 artifactIndexer
.index(node
);
347 String pomSha
= JcrUtils
.checksumFile(node
, "SHA-1");
348 JcrUtils
.copyBytesAsFile(node
.getParent(), node
.getName() + ".sha1",
350 String pomMd5
= JcrUtils
.checksumFile(node
, "MD5");
351 JcrUtils
.copyBytesAsFile(node
.getParent(), node
.getName() + ".md5",
357 private String
generatePomForBundle(Node n
) throws RepositoryException
{
358 String ownSymbolicName
= JcrUtils
.get(n
, SLC_SYMBOLIC_NAME
);
360 StringBuffer p
= new StringBuffer();
363 p
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
364 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");
365 p
.append("<modelVersion>4.0.0</modelVersion>");
368 p
.append("<groupId>").append(JcrUtils
.get(n
, SLC_GROUP_ID
))
369 .append("</groupId>\n");
370 p
.append("<artifactId>").append(JcrUtils
.get(n
, SLC_ARTIFACT_ID
))
371 .append("</artifactId>\n");
372 p
.append("<version>").append(JcrUtils
.get(n
, SLC_ARTIFACT_VERSION
))
373 .append("</version>\n");
374 p
.append("<packaging>pom</packaging>\n");
375 if (n
.hasProperty(SLC_
+ Constants
.BUNDLE_NAME
))
377 .append(JcrUtils
.get(n
, SLC_
+ Constants
.BUNDLE_NAME
))
378 .append("</name>\n");
379 if (n
.hasProperty(SLC_
+ Constants
.BUNDLE_DESCRIPTION
))
380 p
.append("<description>")
382 .get(n
, SLC_
+ Constants
.BUNDLE_DESCRIPTION
))
383 .append("</description>\n");
386 Set
<String
> dependenciesSymbolicNames
= new TreeSet
<String
>();
387 Set
<String
> optionalSymbolicNames
= new TreeSet
<String
>();
388 NodeIterator importPackages
= n
.getNodes(SLC_
389 + Constants
.IMPORT_PACKAGE
);
390 while (importPackages
.hasNext()) {
391 Node importPackage
= importPackages
.nextNode();
392 String pkg
= JcrUtils
.get(importPackage
, SLC_NAME
);
393 if (packagesToSymbolicNames
.containsKey(pkg
)) {
394 String dependencySymbolicName
= packagesToSymbolicNames
396 if (JcrUtils
.check(importPackage
, SLC_OPTIONAL
))
397 optionalSymbolicNames
.add(dependencySymbolicName
);
399 dependenciesSymbolicNames
.add(dependencySymbolicName
);
401 if (!JcrUtils
.check(importPackage
, SLC_OPTIONAL
)
402 && !systemPackages
.contains(pkg
))
403 log
.warn("No bundle found for pkg " + pkg
);
407 if (n
.hasNode(SLC_
+ Constants
.FRAGMENT_HOST
)) {
408 String fragmentHost
= JcrUtils
.get(
409 n
.getNode(SLC_
+ Constants
.FRAGMENT_HOST
),
411 dependenciesSymbolicNames
.add(fragmentHost
);
414 // TODO require bundles
416 List
<Node
> dependencyNodes
= new ArrayList
<Node
>();
417 for (String depSymbName
: dependenciesSymbolicNames
) {
418 if (depSymbName
.equals(ownSymbolicName
))
419 continue;// skip self
421 if (symbolicNamesToNodes
.containsKey(depSymbName
))
422 dependencyNodes
.add(symbolicNamesToNodes
.get(depSymbName
));
424 log
.warn("Could not find node for " + depSymbName
);
426 List
<Node
> optionalDependencyNodes
= new ArrayList
<Node
>();
427 for (String depSymbName
: optionalSymbolicNames
) {
428 if (symbolicNamesToNodes
.containsKey(depSymbName
))
429 optionalDependencyNodes
.add(symbolicNamesToNodes
432 log
.warn("Could not find node for " + depSymbName
);
435 p
.append("<dependencies>\n");
436 for (Node dependencyNode
: dependencyNodes
) {
437 p
.append("<dependency>\n");
438 p
.append("\t<groupId>")
439 .append(JcrUtils
.get(dependencyNode
, SLC_GROUP_ID
))
440 .append("</groupId>\n");
441 p
.append("\t<artifactId>")
442 .append(JcrUtils
.get(dependencyNode
, SLC_ARTIFACT_ID
))
443 .append("</artifactId>\n");
444 p
.append("</dependency>\n");
447 if (optionalDependencyNodes
.size() > 0)
448 p
.append("<!-- OPTIONAL -->\n");
449 for (Node dependencyNode
: optionalDependencyNodes
) {
450 p
.append("<dependency>\n");
451 p
.append("\t<groupId>")
452 .append(JcrUtils
.get(dependencyNode
, SLC_GROUP_ID
))
453 .append("</groupId>\n");
454 p
.append("\t<artifactId>")
455 .append(JcrUtils
.get(dependencyNode
, SLC_ARTIFACT_ID
))
456 .append("</artifactId>\n");
457 p
.append("\t<optional>true</optional>\n");
458 p
.append("</dependency>\n");
460 p
.append("</dependencies>\n");
462 // Dependency management
463 p
.append("<dependencyManagement>\n");
464 p
.append("<dependencies>\n");
465 p
.append("<dependency>\n");
466 p
.append("\t<groupId>").append(groupId
).append("</groupId>\n");
467 p
.append("\t<artifactId>")
468 .append(ownSymbolicName
.endsWith(".source") ? RepoConstants
.SOURCES_ARTIFACT_ID
469 : RepoConstants
.BINARIES_ARTIFACT_ID
)
470 .append("</artifactId>\n");
471 p
.append("\t<version>").append(version
).append("</version>\n");
472 p
.append("\t<type>pom</type>\n");
473 p
.append("\t<scope>import</scope>\n");
474 p
.append("</dependency>\n");
475 p
.append("</dependencies>\n");
476 p
.append("</dependencyManagement>\n");
478 p
.append("</project>\n");
483 public void setRepository(Repository repository
) {
484 this.repository
= repository
;
487 public void setWorkspace(String workspace
) {
488 this.workspace
= workspace
;
491 public void setGroupId(String groupId
) {
492 this.groupId
= groupId
;
495 public void setParentPomCoordinates(String parentPomCoordinates
) {
496 this.parentPomCoordinates
= parentPomCoordinates
;
499 public void setArtifactBasePath(String artifactBasePath
) {
500 this.artifactBasePath
= artifactBasePath
;
503 public void setVersion(String version
) {
504 this.version
= version
;
507 public void setExcludedSuffixes(List
<String
> excludedSuffixes
) {
508 this.excludedSuffixes
= excludedSuffixes
;
511 public void setOverridePoms(Boolean overridePoms
) {
512 this.overridePoms
= overridePoms
;
515 public void setArtifactIndexer(ArtifactIndexer artifactIndexer
) {
516 this.artifactIndexer
= artifactIndexer
;