1 package org
.argeo
.slc
.repo
.osgi
;
3 import java
.util
.ArrayList
;
4 import java
.util
.HashMap
;
8 import java
.util
.StringTokenizer
;
9 import java
.util
.TreeSet
;
11 import javax
.jcr
.Node
;
12 import javax
.jcr
.NodeIterator
;
13 import javax
.jcr
.Repository
;
14 import javax
.jcr
.RepositoryException
;
15 import javax
.jcr
.Session
;
17 import org
.apache
.commons
.io
.FilenameUtils
;
18 import org
.apache
.commons
.logging
.Log
;
19 import org
.apache
.commons
.logging
.LogFactory
;
20 import org
.argeo
.jcr
.JcrMonitor
;
21 import org
.argeo
.jcr
.JcrUtils
;
22 import org
.argeo
.slc
.SlcException
;
23 import org
.argeo
.slc
.SlcNames
;
24 import org
.argeo
.slc
.SlcTypes
;
25 import org
.argeo
.slc
.repo
.ArtifactIndexer
;
26 import org
.argeo
.slc
.repo
.RepoConstants
;
27 import org
.argeo
.slc
.repo
.RepoUtils
;
28 import org
.argeo
.slc
.repo
.maven
.ArtifactIdComparator
;
29 import org
.argeo
.slc
.repo
.maven
.MavenConventionsUtils
;
30 import org
.eclipse
.aether
.artifact
.Artifact
;
31 import org
.eclipse
.aether
.artifact
.DefaultArtifact
;
32 import org
.osgi
.framework
.Constants
;
33 import org
.osgi
.framework
.Version
;
36 * Make sure that all JCR metadata and Maven metadata are consistent for this
37 * group of OSGi bundles.
39 * The job is now done via the various {@code NodeIndexer} of the
40 * WorkspaceManager. TODO import dependencies in the workspace.
43 public class NormalizeGroup
implements Runnable
, SlcNames
{
44 private final static Log log
= LogFactory
.getLog(NormalizeGroup
.class);
46 private Repository repository
;
47 private String workspace
;
48 private String groupId
;
49 private Boolean overridePoms
= false;
50 private String artifactBasePath
= "/";
51 private String version
= null;
52 private String parentPomCoordinates
;
54 private List
<String
> excludedSuffixes
= new ArrayList
<String
>();
56 private ArtifactIndexer artifactIndexer
= new ArtifactIndexer();
57 // private JarFileIndexer jarFileIndexer = new JarFileIndexer();
59 /** TODO make it more generic */
60 private List
<String
> systemPackages
= OsgiProfile
.PROFILE_JAVA_SE_1_6
64 private Map
<String
, String
> packagesToSymbolicNames
= new HashMap
<String
, String
>();
65 private Map
<String
, Node
> symbolicNamesToNodes
= new HashMap
<String
, Node
>();
67 private Set
<Artifact
> binaries
= new TreeSet
<Artifact
>(
68 new ArtifactIdComparator());
69 private Set
<Artifact
> sources
= new TreeSet
<Artifact
>(
70 new ArtifactIdComparator());
73 Session session
= null;
75 session
= repository
.login(workspace
);
76 Node groupNode
= session
.getNode(MavenConventionsUtils
.groupPath(
77 artifactBasePath
, groupId
));
78 processGroupNode(groupNode
, null);
79 } catch (Exception e
) {
80 throw new SlcException("Cannot normalize group " + groupId
+ " in "
83 JcrUtils
.logoutQuietly(session
);
87 public static void processGroupNode(Node groupNode
, String version
,
88 Boolean overridePoms
, JcrMonitor monitor
)
89 throws RepositoryException
{
90 // TODO set artifactsBase based on group node
91 NormalizeGroup ng
= new NormalizeGroup();
92 String groupId
= groupNode
.getProperty(SlcNames
.SLC_GROUP_BASE_ID
)
94 ng
.setGroupId(groupId
);
95 ng
.setVersion(version
);
96 ng
.setOverridePoms(overridePoms
);
97 ng
.processGroupNode(groupNode
, monitor
);
100 protected void processGroupNode(Node groupNode
, JcrMonitor monitor
)
101 throws RepositoryException
{
103 monitor
.subTask("Group " + groupId
);
104 Node allArtifactsHighestVersion
= null;
105 Session session
= groupNode
.getSession();
106 aBases
: for (NodeIterator aBases
= groupNode
.getNodes(); aBases
108 Node aBase
= aBases
.nextNode();
109 if (aBase
.isNodeType(SlcTypes
.SLC_ARTIFACT_BASE
)) {
110 Node highestAVersion
= null;
111 for (NodeIterator aVersions
= aBase
.getNodes(); aVersions
113 Node aVersion
= aVersions
.nextNode();
114 if (aVersion
.isNodeType(SlcTypes
.SLC_ARTIFACT_VERSION_BASE
)) {
115 if (highestAVersion
== null) {
116 highestAVersion
= aVersion
;
117 if (allArtifactsHighestVersion
== null)
118 allArtifactsHighestVersion
= aVersion
;
120 // BS will fail if artifacts arrive in this order
121 // Name1 - V1, name2 - V3, V1 will remain the
122 // allArtifactsHighestVersion
125 Version currVersion
= extractOsgiVersion(aVersion
);
126 Version highestVersion
= extractOsgiVersion(allArtifactsHighestVersion
);
127 if (currVersion
.compareTo(highestVersion
) > 0)
128 allArtifactsHighestVersion
= aVersion
;
132 Version currVersion
= extractOsgiVersion(aVersion
);
133 Version currentHighestVersion
= extractOsgiVersion(highestAVersion
);
134 if (currVersion
.compareTo(currentHighestVersion
) > 0) {
135 highestAVersion
= aVersion
;
138 .compareTo(extractOsgiVersion(allArtifactsHighestVersion
)) > 0) {
139 allArtifactsHighestVersion
= aVersion
;
146 if (highestAVersion
== null)
148 for (NodeIterator files
= highestAVersion
.getNodes(); files
150 Node file
= files
.nextNode();
151 if (file
.isNodeType(SlcTypes
.SLC_BUNDLE_ARTIFACT
)) {
152 preProcessBundleArtifact(file
);
153 file
.getSession().save();
154 if (log
.isDebugEnabled())
155 log
.debug("Pre-processed " + file
.getName());
162 // if version not set or empty, use the highest version
163 // useful when indexing a product maven repository where
164 // all artifacts have the same version for a given release
165 // => the version can then be left empty
166 if (version
== null || version
.trim().equals(""))
167 if (allArtifactsHighestVersion
!= null)
168 version
= allArtifactsHighestVersion
.getProperty(
169 SLC_ARTIFACT_VERSION
).getString();
172 // throw new SlcException("Group version " + version
175 int bundleCount
= symbolicNamesToNodes
.size();
176 if (log
.isDebugEnabled())
177 log
.debug("Indexed " + bundleCount
+ " bundles");
180 for (Node bundleNode
: symbolicNamesToNodes
.values()) {
181 processBundleArtifact(bundleNode
);
182 bundleNode
.getSession().save();
183 if (log
.isDebugEnabled())
184 log
.debug(count
+ "/" + bundleCount
+ " Processed "
185 + bundleNode
.getName());
190 Set
<Artifact
> indexes
= new TreeSet
<Artifact
>(
191 new ArtifactIdComparator());
192 Artifact indexArtifact
= writeIndex(session
,
193 RepoConstants
.BINARIES_ARTIFACT_ID
, binaries
);
194 indexes
.add(indexArtifact
);
195 indexArtifact
= writeIndex(session
, RepoConstants
.SOURCES_ARTIFACT_ID
,
197 indexes
.add(indexArtifact
);
199 writeIndex(session
, RepoConstants
.SDK_ARTIFACT_ID
, indexes
);
204 private Version
extractOsgiVersion(Node artifactVersion
)
205 throws RepositoryException
{
206 String rawVersion
= artifactVersion
.getProperty(SLC_ARTIFACT_VERSION
)
208 String cleanVersion
= rawVersion
.replace("-SNAPSHOT", ".SNAPSHOT");
209 Version osgiVersion
= null;
210 // log invalid version value to enable tracking them
212 osgiVersion
= new Version(cleanVersion
);
213 } catch (IllegalArgumentException e
) {
214 log
.error("Version string " + cleanVersion
+ " is invalid ");
215 String twickedVersion
= twickInvalidVersion(cleanVersion
);
216 osgiVersion
= new Version(twickedVersion
);
217 log
.error("Using " + twickedVersion
+ " instead");
223 private String
twickInvalidVersion(String tmpVersion
) {
224 String
[] tokens
= tmpVersion
.split("\\.");
225 if (tokens
.length
== 3 && tokens
[2].lastIndexOf("-") > 0) {
226 String newSuffix
= tokens
[2].replaceFirst("-", ".");
227 tmpVersion
= tmpVersion
.replaceFirst(tokens
[2], newSuffix
);
228 } else if (tokens
.length
> 4) {
229 // FIXME manually remove other "."
230 StringTokenizer st
= new StringTokenizer(tmpVersion
, ".", true);
231 StringBuilder builder
= new StringBuilder();
233 builder
.append(st
.nextToken()).append(st
.nextToken());
235 builder
.append(st
.nextToken()).append(st
.nextToken());
237 builder
.append(st
.nextToken()).append(st
.nextToken());
239 builder
.append(st
.nextToken());
240 while (st
.hasMoreTokens()) {
243 if (st
.hasMoreTokens())
244 builder
.append("-").append(st
.nextToken());
246 tmpVersion
= builder
.toString();
251 private Artifact
writeIndex(Session session
, String artifactId
,
252 Set
<Artifact
> artifacts
) throws RepositoryException
{
253 Artifact artifact
= new DefaultArtifact(groupId
, artifactId
, "pom",
255 Artifact parentArtifact
= parentPomCoordinates
!= null ?
new DefaultArtifact(
256 parentPomCoordinates
) : null;
257 String pom
= MavenConventionsUtils
.artifactsAsDependencyPom(artifact
,
258 artifacts
, parentArtifact
);
259 Node node
= RepoUtils
.copyBytesAsArtifact(
260 session
.getNode(artifactBasePath
), artifact
, pom
.getBytes());
261 artifactIndexer
.index(node
);
264 String pomSha
= JcrUtils
.checksumFile(node
, "SHA-1");
265 JcrUtils
.copyBytesAsFile(node
.getParent(), node
.getName() + ".sha1",
267 String pomMd5
= JcrUtils
.checksumFile(node
, "MD5");
268 JcrUtils
.copyBytesAsFile(node
.getParent(), node
.getName() + ".md5",
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 bundleNode
.getSession().save();
309 protected void processBundleArtifact(Node bundleNode
)
310 throws RepositoryException
{
311 Node artifactFolder
= bundleNode
.getParent();
312 String baseName
= FilenameUtils
.getBaseName(bundleNode
.getName());
315 String pomName
= baseName
+ ".pom";
316 if (artifactFolder
.hasNode(pomName
) && !overridePoms
)
319 String pom
= generatePomForBundle(bundleNode
);
320 Node pomNode
= JcrUtils
.copyBytesAsFile(artifactFolder
, pomName
,
323 String bundleSha
= JcrUtils
.checksumFile(bundleNode
, "SHA-1");
324 JcrUtils
.copyBytesAsFile(artifactFolder
,
325 bundleNode
.getName() + ".sha1", bundleSha
.getBytes());
326 String pomSha
= JcrUtils
.checksumFile(pomNode
, "SHA-1");
327 JcrUtils
.copyBytesAsFile(artifactFolder
, pomNode
.getName() + ".sha1",
331 private String
generatePomForBundle(Node n
) throws RepositoryException
{
332 String ownSymbolicName
= JcrUtils
.get(n
, SLC_SYMBOLIC_NAME
);
334 StringBuffer p
= new StringBuffer();
337 p
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
338 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");
339 p
.append("<modelVersion>4.0.0</modelVersion>");
342 p
.append("<groupId>").append(JcrUtils
.get(n
, SLC_GROUP_ID
))
343 .append("</groupId>\n");
344 p
.append("<artifactId>").append(JcrUtils
.get(n
, SLC_ARTIFACT_ID
))
345 .append("</artifactId>\n");
346 p
.append("<version>").append(JcrUtils
.get(n
, SLC_ARTIFACT_VERSION
))
347 .append("</version>\n");
348 p
.append("<packaging>pom</packaging>\n");
349 if (n
.hasProperty(SLC_
+ Constants
.BUNDLE_NAME
))
351 .append(JcrUtils
.get(n
, SLC_
+ Constants
.BUNDLE_NAME
))
352 .append("</name>\n");
353 if (n
.hasProperty(SLC_
+ Constants
.BUNDLE_DESCRIPTION
))
354 p
.append("<description>")
356 .get(n
, SLC_
+ Constants
.BUNDLE_DESCRIPTION
))
357 .append("</description>\n");
360 Set
<String
> dependenciesSymbolicNames
= new TreeSet
<String
>();
361 Set
<String
> optionalSymbolicNames
= new TreeSet
<String
>();
362 NodeIterator importPackages
= n
.getNodes(SLC_
363 + Constants
.IMPORT_PACKAGE
);
364 while (importPackages
.hasNext()) {
365 Node importPackage
= importPackages
.nextNode();
366 String pkg
= JcrUtils
.get(importPackage
, SLC_NAME
);
367 if (packagesToSymbolicNames
.containsKey(pkg
)) {
368 String dependencySymbolicName
= packagesToSymbolicNames
370 if (JcrUtils
.check(importPackage
, SLC_OPTIONAL
))
371 optionalSymbolicNames
.add(dependencySymbolicName
);
373 dependenciesSymbolicNames
.add(dependencySymbolicName
);
375 if (!JcrUtils
.check(importPackage
, SLC_OPTIONAL
)
376 && !systemPackages
.contains(pkg
))
377 log
.warn("No bundle found for pkg " + pkg
);
381 if (n
.hasNode(SLC_
+ Constants
.FRAGMENT_HOST
)) {
382 String fragmentHost
= JcrUtils
.get(
383 n
.getNode(SLC_
+ Constants
.FRAGMENT_HOST
),
385 dependenciesSymbolicNames
.add(fragmentHost
);
388 // TODO require bundles
390 List
<Node
> dependencyNodes
= new ArrayList
<Node
>();
391 for (String depSymbName
: dependenciesSymbolicNames
) {
392 if (depSymbName
.equals(ownSymbolicName
))
393 continue;// skip self
395 if (symbolicNamesToNodes
.containsKey(depSymbName
))
396 dependencyNodes
.add(symbolicNamesToNodes
.get(depSymbName
));
398 log
.warn("Could not find node for " + depSymbName
);
400 List
<Node
> optionalDependencyNodes
= new ArrayList
<Node
>();
401 for (String depSymbName
: optionalSymbolicNames
) {
402 if (symbolicNamesToNodes
.containsKey(depSymbName
))
403 optionalDependencyNodes
.add(symbolicNamesToNodes
406 log
.warn("Could not find node for " + depSymbName
);
409 p
.append("<dependencies>\n");
410 for (Node dependencyNode
: dependencyNodes
) {
411 p
.append("<dependency>\n");
412 p
.append("\t<groupId>")
413 .append(JcrUtils
.get(dependencyNode
, SLC_GROUP_ID
))
414 .append("</groupId>\n");
415 p
.append("\t<artifactId>")
416 .append(JcrUtils
.get(dependencyNode
, SLC_ARTIFACT_ID
))
417 .append("</artifactId>\n");
418 p
.append("</dependency>\n");
421 if (optionalDependencyNodes
.size() > 0)
422 p
.append("<!-- OPTIONAL -->\n");
423 for (Node dependencyNode
: optionalDependencyNodes
) {
424 p
.append("<dependency>\n");
425 p
.append("\t<groupId>")
426 .append(JcrUtils
.get(dependencyNode
, SLC_GROUP_ID
))
427 .append("</groupId>\n");
428 p
.append("\t<artifactId>")
429 .append(JcrUtils
.get(dependencyNode
, SLC_ARTIFACT_ID
))
430 .append("</artifactId>\n");
431 p
.append("\t<optional>true</optional>\n");
432 p
.append("</dependency>\n");
434 p
.append("</dependencies>\n");
436 // Dependency management
437 p
.append("<dependencyManagement>\n");
438 p
.append("<dependencies>\n");
439 p
.append("<dependency>\n");
440 p
.append("\t<groupId>").append(groupId
).append("</groupId>\n");
441 p
.append("\t<artifactId>")
442 .append(ownSymbolicName
.endsWith(".source") ? RepoConstants
.SOURCES_ARTIFACT_ID
443 : RepoConstants
.BINARIES_ARTIFACT_ID
)
444 .append("</artifactId>\n");
445 p
.append("\t<version>").append(version
).append("</version>\n");
446 p
.append("\t<type>pom</type>\n");
447 p
.append("\t<scope>import</scope>\n");
448 p
.append("</dependency>\n");
449 p
.append("</dependencies>\n");
450 p
.append("</dependencyManagement>\n");
452 p
.append("</project>\n");
456 /* DEPENDENCY INJECTION */
457 public void setRepository(Repository repository
) {
458 this.repository
= repository
;
461 public void setWorkspace(String workspace
) {
462 this.workspace
= workspace
;
465 public void setGroupId(String groupId
) {
466 this.groupId
= groupId
;
469 public void setParentPomCoordinates(String parentPomCoordinates
) {
470 this.parentPomCoordinates
= parentPomCoordinates
;
473 public void setArtifactBasePath(String artifactBasePath
) {
474 this.artifactBasePath
= artifactBasePath
;
477 public void setVersion(String version
) {
478 this.version
= version
;
481 public void setExcludedSuffixes(List
<String
> excludedSuffixes
) {
482 this.excludedSuffixes
= excludedSuffixes
;
485 public void setOverridePoms(Boolean overridePoms
) {
486 this.overridePoms
= overridePoms
;
489 public void setArtifactIndexer(ArtifactIndexer artifactIndexer
) {
490 this.artifactIndexer
= artifactIndexer
;