]> git.argeo.org Git - gpl/argeo-slc.git/blob - org.argeo.slc.repo/src/org/argeo/slc/repo/osgi/NormalizeGroup.java
Adapt to changes in Argeo Commons
[gpl/argeo-slc.git] / org.argeo.slc.repo / src / org / argeo / slc / repo / osgi / NormalizeGroup.java
1 package org.argeo.slc.repo.osgi;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Set;
8 import java.util.StringTokenizer;
9 import java.util.TreeSet;
10
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;
16
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;
34
35 /**
36 * Make sure that all JCR metadata and Maven metadata are consistent for this
37 * group of OSGi bundles.
38 *
39 * The job is now done via the various {@code NodeIndexer} of the
40 * WorkspaceManager. TODO import dependencies in the workspace.
41 */
42 @Deprecated
43 public class NormalizeGroup implements Runnable, SlcNames {
44 private final static Log log = LogFactory.getLog(NormalizeGroup.class);
45
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;
53
54 private List<String> excludedSuffixes = new ArrayList<String>();
55
56 private ArtifactIndexer artifactIndexer = new ArtifactIndexer();
57 // private JarFileIndexer jarFileIndexer = new JarFileIndexer();
58
59 /** TODO make it more generic */
60 private List<String> systemPackages = OsgiProfile.PROFILE_JAVA_SE_1_6
61 .getSystemPackages();
62
63 // indexes
64 private Map<String, String> packagesToSymbolicNames = new HashMap<String, String>();
65 private Map<String, Node> symbolicNamesToNodes = new HashMap<String, Node>();
66
67 private Set<Artifact> binaries = new TreeSet<Artifact>(
68 new ArtifactIdComparator());
69 private Set<Artifact> sources = new TreeSet<Artifact>(
70 new ArtifactIdComparator());
71
72 public void run() {
73 Session session = null;
74 try {
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 "
81 + workspace, e);
82 } finally {
83 JcrUtils.logoutQuietly(session);
84 }
85 }
86
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)
93 .getString();
94 ng.setGroupId(groupId);
95 ng.setVersion(version);
96 ng.setOverridePoms(overridePoms);
97 ng.processGroupNode(groupNode, monitor);
98 }
99
100 protected void processGroupNode(Node groupNode, JcrMonitor monitor)
101 throws RepositoryException {
102 if (monitor != null)
103 monitor.subTask("Group " + groupId);
104 Node allArtifactsHighestVersion = null;
105 Session session = groupNode.getSession();
106 aBases: for (NodeIterator aBases = groupNode.getNodes(); aBases
107 .hasNext();) {
108 Node aBase = aBases.nextNode();
109 if (aBase.isNodeType(SlcTypes.SLC_ARTIFACT_BASE)) {
110 Node highestAVersion = null;
111 for (NodeIterator aVersions = aBase.getNodes(); aVersions
112 .hasNext();) {
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;
119
120 // BS will fail if artifacts arrive in this order
121 // Name1 - V1, name2 - V3, V1 will remain the
122 // allArtifactsHighestVersion
123 // Fixed below
124 else {
125 Version currVersion = extractOsgiVersion(aVersion);
126 Version highestVersion = extractOsgiVersion(allArtifactsHighestVersion);
127 if (currVersion.compareTo(highestVersion) > 0)
128 allArtifactsHighestVersion = aVersion;
129 }
130
131 } else {
132 Version currVersion = extractOsgiVersion(aVersion);
133 Version currentHighestVersion = extractOsgiVersion(highestAVersion);
134 if (currVersion.compareTo(currentHighestVersion) > 0) {
135 highestAVersion = aVersion;
136 }
137 if (currVersion
138 .compareTo(extractOsgiVersion(allArtifactsHighestVersion)) > 0) {
139 allArtifactsHighestVersion = aVersion;
140 }
141 }
142
143 }
144
145 }
146 if (highestAVersion == null)
147 continue aBases;
148 for (NodeIterator files = highestAVersion.getNodes(); files
149 .hasNext();) {
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());
156 }
157
158 }
159 }
160 }
161
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();
170 else
171 version = "0.0";
172 // throw new SlcException("Group version " + version
173 // + " is empty.");
174
175 int bundleCount = symbolicNamesToNodes.size();
176 if (log.isDebugEnabled())
177 log.debug("Indexed " + bundleCount + " bundles");
178
179 int count = 1;
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());
186 count++;
187 }
188
189 // indexes
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,
196 sources);
197 indexes.add(indexArtifact);
198 // sdk
199 writeIndex(session, RepoConstants.SDK_ARTIFACT_ID, indexes);
200 if (monitor != null)
201 monitor.worked(1);
202 }
203
204 private Version extractOsgiVersion(Node artifactVersion)
205 throws RepositoryException {
206 String rawVersion = artifactVersion.getProperty(SLC_ARTIFACT_VERSION)
207 .getString();
208 String cleanVersion = rawVersion.replace("-SNAPSHOT", ".SNAPSHOT");
209 Version osgiVersion = null;
210 // log invalid version value to enable tracking them
211 try {
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");
218 // throw e;
219 }
220 return osgiVersion;
221 }
222
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();
232 // Major
233 builder.append(st.nextToken()).append(st.nextToken());
234 // Minor
235 builder.append(st.nextToken()).append(st.nextToken());
236 // Micro
237 builder.append(st.nextToken()).append(st.nextToken());
238 // Qualifier
239 builder.append(st.nextToken());
240 while (st.hasMoreTokens()) {
241 // consume delimiter
242 st.nextToken();
243 if (st.hasMoreTokens())
244 builder.append("-").append(st.nextToken());
245 }
246 tmpVersion = builder.toString();
247 }
248 return tmpVersion;
249 }
250
251 private Artifact writeIndex(Session session, String artifactId,
252 Set<Artifact> artifacts) throws RepositoryException {
253 Artifact artifact = new DefaultArtifact(groupId, artifactId, "pom",
254 version);
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);
262
263 // TODO factorize
264 String pomSha = JcrUtils.checksumFile(node, "SHA-1");
265 JcrUtils.copyBytesAsFile(node.getParent(), node.getName() + ".sha1",
266 pomSha.getBytes());
267 String pomMd5 = JcrUtils.checksumFile(node, "MD5");
268 JcrUtils.copyBytesAsFile(node.getParent(), node.getName() + ".md5",
269 pomMd5.getBytes());
270 session.save();
271 return artifact;
272 }
273
274 protected void preProcessBundleArtifact(Node bundleNode)
275 throws RepositoryException {
276
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
285 }
286 sources.add(RepoUtils.asArtifact(bundleNode));
287 return;
288 }
289
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);
296 }
297
298 symbolicNamesToNodes.put(symbolicName, bundleNode);
299 for (String excludedSuffix : excludedSuffixes) {
300 if (symbolicName.endsWith(excludedSuffix))
301 return;// skip adding to binaries
302 }
303 binaries.add(RepoUtils.asArtifact(bundleNode));
304
305 if (bundleNode.getSession().hasPendingChanges())
306 bundleNode.getSession().save();
307 }
308
309 protected void processBundleArtifact(Node bundleNode)
310 throws RepositoryException {
311 Node artifactFolder = bundleNode.getParent();
312 String baseName = FilenameUtils.getBaseName(bundleNode.getName());
313
314 // pom
315 String pomName = baseName + ".pom";
316 if (artifactFolder.hasNode(pomName) && !overridePoms)
317 return;// skip
318
319 String pom = generatePomForBundle(bundleNode);
320 Node pomNode = JcrUtils.copyBytesAsFile(artifactFolder, pomName,
321 pom.getBytes());
322 // checksum
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",
328 pomSha.getBytes());
329 }
330
331 private String generatePomForBundle(Node n) throws RepositoryException {
332 String ownSymbolicName = JcrUtils.get(n, SLC_SYMBOLIC_NAME);
333
334 StringBuffer p = new StringBuffer();
335
336 // XML header
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>");
340
341 // Artifact
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))
350 p.append("<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>")
355 .append(JcrUtils
356 .get(n, SLC_ + Constants.BUNDLE_DESCRIPTION))
357 .append("</description>\n");
358
359 // Dependencies
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
369 .get(pkg);
370 if (JcrUtils.check(importPackage, SLC_OPTIONAL))
371 optionalSymbolicNames.add(dependencySymbolicName);
372 else
373 dependenciesSymbolicNames.add(dependencySymbolicName);
374 } else {
375 if (!JcrUtils.check(importPackage, SLC_OPTIONAL)
376 && !systemPackages.contains(pkg))
377 log.warn("No bundle found for pkg " + pkg);
378 }
379 }
380
381 if (n.hasNode(SLC_ + Constants.FRAGMENT_HOST)) {
382 String fragmentHost = JcrUtils.get(
383 n.getNode(SLC_ + Constants.FRAGMENT_HOST),
384 SLC_SYMBOLIC_NAME);
385 dependenciesSymbolicNames.add(fragmentHost);
386 }
387
388 // TODO require bundles
389
390 List<Node> dependencyNodes = new ArrayList<Node>();
391 for (String depSymbName : dependenciesSymbolicNames) {
392 if (depSymbName.equals(ownSymbolicName))
393 continue;// skip self
394
395 if (symbolicNamesToNodes.containsKey(depSymbName))
396 dependencyNodes.add(symbolicNamesToNodes.get(depSymbName));
397 else
398 log.warn("Could not find node for " + depSymbName);
399 }
400 List<Node> optionalDependencyNodes = new ArrayList<Node>();
401 for (String depSymbName : optionalSymbolicNames) {
402 if (symbolicNamesToNodes.containsKey(depSymbName))
403 optionalDependencyNodes.add(symbolicNamesToNodes
404 .get(depSymbName));
405 else
406 log.warn("Could not find node for " + depSymbName);
407 }
408
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");
419 }
420
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");
433 }
434 p.append("</dependencies>\n");
435
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");
451
452 p.append("</project>\n");
453 return p.toString();
454 }
455
456 /* DEPENDENCY INJECTION */
457 public void setRepository(Repository repository) {
458 this.repository = repository;
459 }
460
461 public void setWorkspace(String workspace) {
462 this.workspace = workspace;
463 }
464
465 public void setGroupId(String groupId) {
466 this.groupId = groupId;
467 }
468
469 public void setParentPomCoordinates(String parentPomCoordinates) {
470 this.parentPomCoordinates = parentPomCoordinates;
471 }
472
473 public void setArtifactBasePath(String artifactBasePath) {
474 this.artifactBasePath = artifactBasePath;
475 }
476
477 public void setVersion(String version) {
478 this.version = version;
479 }
480
481 public void setExcludedSuffixes(List<String> excludedSuffixes) {
482 this.excludedSuffixes = excludedSuffixes;
483 }
484
485 public void setOverridePoms(Boolean overridePoms) {
486 this.overridePoms = overridePoms;
487 }
488
489 public void setArtifactIndexer(ArtifactIndexer artifactIndexer) {
490 this.artifactIndexer = artifactIndexer;
491 }
492 }