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
.osgi
;
18 import java
.util
.ArrayList
;
19 import java
.util
.HashMap
;
20 import java
.util
.List
;
23 import java
.util
.TreeSet
;
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
.io
.FilenameUtils
;
32 import org
.apache
.commons
.logging
.Log
;
33 import org
.apache
.commons
.logging
.LogFactory
;
34 import org
.argeo
.ArgeoMonitor
;
35 import org
.argeo
.jcr
.JcrUtils
;
36 import org
.argeo
.slc
.SlcException
;
37 import org
.argeo
.slc
.aether
.ArtifactIdComparator
;
38 import org
.argeo
.slc
.jcr
.SlcNames
;
39 import org
.argeo
.slc
.jcr
.SlcTypes
;
40 import org
.argeo
.slc
.repo
.ArtifactIndexer
;
41 import org
.argeo
.slc
.repo
.RepoUtils
;
42 import org
.argeo
.slc
.repo
.maven
.MavenConventionsUtils
;
43 import org
.osgi
.framework
.Constants
;
44 import org
.osgi
.framework
.Version
;
45 import org
.sonatype
.aether
.artifact
.Artifact
;
46 import org
.sonatype
.aether
.util
.artifact
.DefaultArtifact
;
49 * Make sure that all JCR metadata and Maven metadata are consistent for this
50 * group of OSGi bundles.
52 public class NormalizeGroup
implements Runnable
, SlcNames
{
53 public final static String BINARIES_ARTIFACT_ID
= "binaries";
54 public final static String SOURCES_ARTIFACT_ID
= "sources";
55 public final static String SDK_ARTIFACT_ID
= "sdk";
57 private final static Log log
= LogFactory
.getLog(NormalizeGroup
.class);
59 private Repository repository
;
60 private String workspace
;
61 private String groupId
;
62 private Boolean overridePoms
= false;
63 private String artifactBasePath
= "/";
64 private String version
= null;
65 private String parentPomCoordinates
;
67 private List
<String
> excludedSuffixes
= new ArrayList
<String
>();
69 private ArtifactIndexer artifactIndexer
= new ArtifactIndexer();
70 // private JarFileIndexer jarFileIndexer = new JarFileIndexer();
72 /** TODO make it more generic */
73 private List
<String
> systemPackages
= OsgiProfile
.PROFILE_JAVA_SE_1_6
77 private Map
<String
, String
> packagesToSymbolicNames
= new HashMap
<String
, String
>();
78 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());
86 Session session
= null;
88 session
= repository
.login(workspace
);
89 Node groupNode
= session
.getNode(MavenConventionsUtils
.groupPath(
90 artifactBasePath
, groupId
));
91 processGroupNode(groupNode
, null);
92 } catch (Exception e
) {
93 throw new SlcException("Cannot normalize group " + groupId
+ " in "
96 JcrUtils
.logoutQuietly(session
);
100 public static void processGroupNode(Node groupNode
, String version
,
101 Boolean overridePoms
, ArgeoMonitor monitor
)
102 throws RepositoryException
{
103 // TODO set artifactsBase based on group node
104 NormalizeGroup ng
= new NormalizeGroup();
105 String groupId
= groupNode
.getProperty(SlcNames
.SLC_GROUP_BASE_ID
)
107 ng
.setGroupId(groupId
);
108 ng
.setVersion(version
);
109 ng
.setOverridePoms(overridePoms
);
110 ng
.processGroupNode(groupNode
, monitor
);
113 protected void processGroupNode(Node groupNode
, ArgeoMonitor monitor
)
114 throws RepositoryException
{
116 monitor
.subTask("Group " + groupId
);
117 Node allArtifactsHighestVersion
= null;
118 Session session
= groupNode
.getSession();
119 aBases
: for (NodeIterator aBases
= groupNode
.getNodes(); aBases
121 Node aBase
= aBases
.nextNode();
122 if (aBase
.isNodeType(SlcTypes
.SLC_ARTIFACT_BASE
)) {
123 Node highestAVersion
= null;
124 for (NodeIterator aVersions
= aBase
.getNodes(); aVersions
126 Node aVersion
= aVersions
.nextNode();
127 if (aVersion
.isNodeType(SlcTypes
.SLC_ARTIFACT_VERSION_BASE
)) {
128 if (highestAVersion
== null) {
129 highestAVersion
= aVersion
;
130 if (allArtifactsHighestVersion
== null)
131 allArtifactsHighestVersion
= aVersion
;
134 Version currVersion
= extractOsgiVersion(aVersion
);
135 Version currentHighestVersion
= extractOsgiVersion(highestAVersion
);
136 if (currVersion
.compareTo(currentHighestVersion
) > 0) {
137 highestAVersion
= aVersion
;
140 .compareTo(extractOsgiVersion(allArtifactsHighestVersion
)) > 0) {
141 allArtifactsHighestVersion
= aVersion
;
148 if (highestAVersion
== null)
150 for (NodeIterator files
= highestAVersion
.getNodes(); files
152 Node file
= files
.nextNode();
153 if (file
.isNodeType(SlcTypes
.SLC_BUNDLE_ARTIFACT
)) {
154 preProcessBundleArtifact(file
);
155 file
.getSession().save();
156 if (log
.isDebugEnabled())
157 log
.debug("Pre-processed " + file
.getName());
164 // if version not set or empty, use the highets version
165 // useful when indexing a product maven repository where
166 // all artifacts have the same version for a given release
167 // => the version can then be left empty
168 if (version
== null || version
.trim().equals(""))
169 if (allArtifactsHighestVersion
!= null)
170 version
= allArtifactsHighestVersion
.getProperty(
171 SLC_ARTIFACT_VERSION
).getString();
173 throw new SlcException("Group version " + version
176 int bundleCount
= symbolicNamesToNodes
.size();
177 if (log
.isDebugEnabled())
178 log
.debug("Indexed " + bundleCount
+ " bundles");
181 for (Node bundleNode
: symbolicNamesToNodes
.values()) {
182 processBundleArtifact(bundleNode
);
183 bundleNode
.getSession().save();
184 if (log
.isDebugEnabled())
185 log
.debug(count
+ "/" + bundleCount
+ " Processed "
186 + bundleNode
.getName());
191 Set
<Artifact
> indexes
= new TreeSet
<Artifact
>(
192 new ArtifactIdComparator());
193 Artifact indexArtifact
= writeIndex(session
, BINARIES_ARTIFACT_ID
,
195 indexes
.add(indexArtifact
);
196 indexArtifact
= writeIndex(session
, SOURCES_ARTIFACT_ID
, sources
);
197 indexes
.add(indexArtifact
);
199 writeIndex(session
, 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 return new Version(cleanVersion
);
212 private Artifact
writeIndex(Session session
, String artifactId
,
213 Set
<Artifact
> artifacts
) throws RepositoryException
{
214 Artifact artifact
= new DefaultArtifact(groupId
, artifactId
, "pom",
216 Artifact parentArtifact
= parentPomCoordinates
!= null ?
new DefaultArtifact(
217 parentPomCoordinates
) : null;
218 String pom
= MavenConventionsUtils
.artifactsAsDependencyPom(artifact
,
219 artifacts
, parentArtifact
);
220 Node node
= RepoUtils
.copyBytesAsArtifact(
221 session
.getNode(artifactBasePath
), artifact
, pom
.getBytes());
222 artifactIndexer
.index(node
);
225 String pomSha
= JcrUtils
.checksumFile(node
, "SHA-1");
226 JcrUtils
.copyBytesAsFile(node
.getParent(), node
.getName() + ".sha1",
228 String pomMd5
= JcrUtils
.checksumFile(node
, "MD5");
229 JcrUtils
.copyBytesAsFile(node
.getParent(), node
.getName() + ".md5",
235 protected void preProcessBundleArtifact(Node bundleNode
)
236 throws RepositoryException
{
237 // we assume nodes are already indexed
238 // artifactIndexer.index(bundleNode);
239 // jarFileIndexer.index(bundleNode);
241 String symbolicName
= JcrUtils
.get(bundleNode
, SLC_SYMBOLIC_NAME
);
243 if (symbolicName
.endsWith(".source")) {
244 // TODO make a shared node with classifier 'sources'?
245 String bundleName
= RepoUtils
246 .extractBundleNameFromSourceName(symbolicName
);
247 for (String excludedSuffix
: excludedSuffixes
) {
248 if (bundleName
.endsWith(excludedSuffix
))
249 return;// skip adding to sources
251 sources
.add(RepoUtils
.asArtifact(bundleNode
));
255 NodeIterator exportPackages
= bundleNode
.getNodes(SLC_
256 + Constants
.EXPORT_PACKAGE
);
257 while (exportPackages
.hasNext()) {
258 Node exportPackage
= exportPackages
.nextNode();
259 String pkg
= JcrUtils
.get(exportPackage
, SLC_NAME
);
260 packagesToSymbolicNames
.put(pkg
, symbolicName
);
263 symbolicNamesToNodes
.put(symbolicName
, bundleNode
);
264 for (String excludedSuffix
: excludedSuffixes
) {
265 if (symbolicName
.endsWith(excludedSuffix
))
266 return;// skip adding to binaries
268 binaries
.add(RepoUtils
.asArtifact(bundleNode
));
270 if (bundleNode
.getSession().hasPendingChanges())
271 bundleNode
.getSession().save();
274 protected void processBundleArtifact(Node bundleNode
)
275 throws RepositoryException
{
276 Node artifactFolder
= bundleNode
.getParent();
277 String baseName
= FilenameUtils
.getBaseName(bundleNode
.getName());
280 String pomName
= baseName
+ ".pom";
281 if (artifactFolder
.hasNode(pomName
) && !overridePoms
)
284 String pom
= generatePomForBundle(bundleNode
);
285 Node pomNode
= JcrUtils
.copyBytesAsFile(artifactFolder
, pomName
,
288 String bundleSha
= JcrUtils
.checksumFile(bundleNode
, "SHA-1");
289 JcrUtils
.copyBytesAsFile(artifactFolder
,
290 bundleNode
.getName() + ".sha1", bundleSha
.getBytes());
291 String pomSha
= JcrUtils
.checksumFile(pomNode
, "SHA-1");
292 JcrUtils
.copyBytesAsFile(artifactFolder
, pomNode
.getName() + ".sha1",
296 private String
generatePomForBundle(Node n
) throws RepositoryException
{
297 String ownSymbolicName
= JcrUtils
.get(n
, SLC_SYMBOLIC_NAME
);
299 StringBuffer p
= new StringBuffer();
302 p
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
303 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");
304 p
.append("<modelVersion>4.0.0</modelVersion>");
307 // p.append("<parent><groupId>org.argeo</groupId><artifactId>parent</artifactId><version>1.2.0</version></parent>\n");
308 p
.append("<groupId>").append(JcrUtils
.get(n
, SLC_GROUP_ID
))
309 .append("</groupId>\n");
310 p
.append("<artifactId>").append(JcrUtils
.get(n
, SLC_ARTIFACT_ID
))
311 .append("</artifactId>\n");
312 p
.append("<version>").append(JcrUtils
.get(n
, SLC_ARTIFACT_VERSION
))
313 .append("</version>\n");
314 p
.append("<packaging>pom</packaging>\n");
315 if (n
.hasProperty(SLC_
+ Constants
.BUNDLE_NAME
))
317 .append(JcrUtils
.get(n
, SLC_
+ Constants
.BUNDLE_NAME
))
318 .append("</name>\n");
319 if (n
.hasProperty(SLC_
+ Constants
.BUNDLE_DESCRIPTION
))
320 p
.append("<description>")
322 .get(n
, SLC_
+ Constants
.BUNDLE_DESCRIPTION
))
323 .append("</description>\n");
326 Set
<String
> dependenciesSymbolicNames
= new TreeSet
<String
>();
327 Set
<String
> optionalSymbolicNames
= new TreeSet
<String
>();
328 NodeIterator importPackages
= n
.getNodes(SLC_
329 + Constants
.IMPORT_PACKAGE
);
330 while (importPackages
.hasNext()) {
331 Node importPackage
= importPackages
.nextNode();
332 String pkg
= JcrUtils
.get(importPackage
, SLC_NAME
);
333 if (packagesToSymbolicNames
.containsKey(pkg
)) {
334 String dependencySymbolicName
= packagesToSymbolicNames
336 if (JcrUtils
.check(importPackage
, SLC_OPTIONAL
))
337 optionalSymbolicNames
.add(dependencySymbolicName
);
339 dependenciesSymbolicNames
.add(dependencySymbolicName
);
341 if (!JcrUtils
.check(importPackage
, SLC_OPTIONAL
)
342 && !systemPackages
.contains(pkg
))
343 log
.warn("No bundle found for pkg " + pkg
);
347 if (n
.hasNode(SLC_
+ Constants
.FRAGMENT_HOST
)) {
348 String fragmentHost
= JcrUtils
.get(
349 n
.getNode(SLC_
+ Constants
.FRAGMENT_HOST
),
351 dependenciesSymbolicNames
.add(fragmentHost
);
354 // TODO require bundles
356 List
<Node
> dependencyNodes
= new ArrayList
<Node
>();
357 for (String depSymbName
: dependenciesSymbolicNames
) {
358 if (depSymbName
.equals(ownSymbolicName
))
359 continue;// skip self
361 if (symbolicNamesToNodes
.containsKey(depSymbName
))
362 dependencyNodes
.add(symbolicNamesToNodes
.get(depSymbName
));
364 log
.warn("Could not find node for " + depSymbName
);
366 List
<Node
> optionalDependencyNodes
= new ArrayList
<Node
>();
367 for (String depSymbName
: optionalSymbolicNames
) {
368 if (symbolicNamesToNodes
.containsKey(depSymbName
))
369 optionalDependencyNodes
.add(symbolicNamesToNodes
372 log
.warn("Could not find node for " + depSymbName
);
375 p
.append("<dependencies>\n");
376 for (Node dependencyNode
: dependencyNodes
) {
377 p
.append("<dependency>\n");
378 p
.append("\t<groupId>")
379 .append(JcrUtils
.get(dependencyNode
, SLC_GROUP_ID
))
380 .append("</groupId>\n");
381 p
.append("\t<artifactId>")
382 .append(JcrUtils
.get(dependencyNode
, SLC_ARTIFACT_ID
))
383 .append("</artifactId>\n");
384 p
.append("</dependency>\n");
387 if (optionalDependencyNodes
.size() > 0)
388 p
.append("<!-- OPTIONAL -->\n");
389 for (Node dependencyNode
: optionalDependencyNodes
) {
390 p
.append("<dependency>\n");
391 p
.append("\t<groupId>")
392 .append(JcrUtils
.get(dependencyNode
, SLC_GROUP_ID
))
393 .append("</groupId>\n");
394 p
.append("\t<artifactId>")
395 .append(JcrUtils
.get(dependencyNode
, SLC_ARTIFACT_ID
))
396 .append("</artifactId>\n");
397 p
.append("\t<optional>true</optional>\n");
398 p
.append("</dependency>\n");
400 p
.append("</dependencies>\n");
402 // Dependency management
403 p
.append("<dependencyManagement>\n");
404 p
.append("<dependencies>\n");
405 p
.append("<dependency>\n");
406 p
.append("\t<groupId>").append(groupId
).append("</groupId>\n");
407 p
.append("\t<artifactId>")
408 .append(ownSymbolicName
.endsWith(".source") ? SOURCES_ARTIFACT_ID
409 : BINARIES_ARTIFACT_ID
).append("</artifactId>\n");
410 p
.append("\t<version>").append(version
).append("</version>\n");
411 p
.append("\t<type>pom</type>\n");
412 p
.append("\t<scope>import</scope>\n");
413 p
.append("</dependency>\n");
414 p
.append("</dependencies>\n");
415 p
.append("</dependencyManagement>\n");
417 p
.append("</project>\n");
421 public void setRepository(Repository repository
) {
422 this.repository
= repository
;
425 public void setWorkspace(String workspace
) {
426 this.workspace
= workspace
;
429 public void setGroupId(String groupId
) {
430 this.groupId
= groupId
;
433 public void setParentPomCoordinates(String parentPomCoordinates
) {
434 this.parentPomCoordinates
= parentPomCoordinates
;
437 public void setArtifactBasePath(String artifactBasePath
) {
438 this.artifactBasePath
= artifactBasePath
;
441 public void setVersion(String version
) {
442 this.version
= version
;
445 public void setExcludedSuffixes(List
<String
> excludedSuffixes
) {
446 this.excludedSuffixes
= excludedSuffixes
;
449 public void setOverridePoms(Boolean overridePoms
) {
450 this.overridePoms
= overridePoms
;
453 public void setArtifactIndexer(ArtifactIndexer artifactIndexer
) {
454 this.artifactIndexer
= artifactIndexer
;