]> git.argeo.org Git - gpl/argeo-slc.git/blob - org.argeo.slc.repo/src/org/argeo/slc/repo/osgi/NormalizeGroup.java
Start working on A2 factory.
[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.argeo.api.cms.CmsLog;
19 import org.argeo.jcr.JcrMonitor;
20 import org.argeo.jcr.JcrUtils;
21 import org.argeo.slc.SlcException;
22 import org.argeo.slc.SlcNames;
23 import org.argeo.slc.SlcTypes;
24 import org.argeo.slc.repo.ArtifactIndexer;
25 import org.argeo.slc.repo.RepoConstants;
26 import org.argeo.slc.repo.RepoUtils;
27 import org.argeo.slc.repo.maven.ArtifactIdComparator;
28 import org.argeo.slc.repo.maven.MavenConventionsUtils;
29 import org.eclipse.aether.artifact.Artifact;
30 import org.eclipse.aether.artifact.DefaultArtifact;
31 import org.osgi.framework.Constants;
32 import org.osgi.framework.Version;
33
34 /**
35 * Make sure that all JCR metadata and Maven metadata are consistent for this
36 * group of OSGi bundles.
37 *
38 * The job is now done via the various {@code NodeIndexer} of the
39 * WorkspaceManager. TODO import dependencies in the workspace.
40 */
41 @Deprecated
42 public class NormalizeGroup implements Runnable, SlcNames {
43 private final static CmsLog log = CmsLog.getLog(NormalizeGroup.class);
44
45 private Repository repository;
46 private String workspace;
47 private String groupId;
48 private Boolean overridePoms = false;
49 private String artifactBasePath = "/";
50 private String version = null;
51 private String parentPomCoordinates;
52
53 private List<String> excludedSuffixes = new ArrayList<String>();
54
55 private ArtifactIndexer artifactIndexer = new ArtifactIndexer();
56 // private JarFileIndexer jarFileIndexer = new JarFileIndexer();
57
58 /** TODO make it more generic */
59 private List<String> systemPackages = OsgiProfile.PROFILE_JAVA_SE_1_6.getSystemPackages();
60
61 // indexes
62 private Map<String, String> packagesToSymbolicNames = new HashMap<String, String>();
63 private Map<String, Node> symbolicNamesToNodes = new HashMap<String, Node>();
64
65 private Set<Artifact> binaries = new TreeSet<Artifact>(new ArtifactIdComparator());
66 private Set<Artifact> sources = new TreeSet<Artifact>(new ArtifactIdComparator());
67
68 public void run() {
69 Session session = null;
70 try {
71 session = repository.login(workspace);
72 Node groupNode = session.getNode(MavenConventionsUtils.groupPath(artifactBasePath, groupId));
73 processGroupNode(groupNode, null);
74 } catch (Exception e) {
75 throw new SlcException("Cannot normalize group " + groupId + " in " + workspace, e);
76 } finally {
77 JcrUtils.logoutQuietly(session);
78 }
79 }
80
81 public static void processGroupNode(Node groupNode, String version, Boolean overridePoms, JcrMonitor monitor)
82 throws RepositoryException {
83 // TODO set artifactsBase based on group node
84 NormalizeGroup ng = new NormalizeGroup();
85 String groupId = groupNode.getProperty(SlcNames.SLC_GROUP_BASE_ID).getString();
86 ng.setGroupId(groupId);
87 ng.setVersion(version);
88 ng.setOverridePoms(overridePoms);
89 ng.processGroupNode(groupNode, monitor);
90 }
91
92 protected void processGroupNode(Node groupNode, JcrMonitor monitor) throws RepositoryException {
93 if (monitor != null)
94 monitor.subTask("Group " + groupId);
95 Node allArtifactsHighestVersion = null;
96 Session session = groupNode.getSession();
97 aBases: for (NodeIterator aBases = groupNode.getNodes(); aBases.hasNext();) {
98 Node aBase = aBases.nextNode();
99 if (aBase.isNodeType(SlcTypes.SLC_ARTIFACT_BASE)) {
100 Node highestAVersion = null;
101 for (NodeIterator aVersions = aBase.getNodes(); aVersions.hasNext();) {
102 Node aVersion = aVersions.nextNode();
103 if (aVersion.isNodeType(SlcTypes.SLC_ARTIFACT_VERSION_BASE)) {
104 if (highestAVersion == null) {
105 highestAVersion = aVersion;
106 if (allArtifactsHighestVersion == null)
107 allArtifactsHighestVersion = aVersion;
108
109 // BS will fail if artifacts arrive in this order
110 // Name1 - V1, name2 - V3, V1 will remain the
111 // allArtifactsHighestVersion
112 // Fixed below
113 else {
114 Version currVersion = extractOsgiVersion(aVersion);
115 Version highestVersion = extractOsgiVersion(allArtifactsHighestVersion);
116 if (currVersion.compareTo(highestVersion) > 0)
117 allArtifactsHighestVersion = aVersion;
118 }
119
120 } else {
121 Version currVersion = extractOsgiVersion(aVersion);
122 Version currentHighestVersion = extractOsgiVersion(highestAVersion);
123 if (currVersion.compareTo(currentHighestVersion) > 0) {
124 highestAVersion = aVersion;
125 }
126 if (currVersion.compareTo(extractOsgiVersion(allArtifactsHighestVersion)) > 0) {
127 allArtifactsHighestVersion = aVersion;
128 }
129 }
130
131 }
132
133 }
134 if (highestAVersion == null)
135 continue aBases;
136 for (NodeIterator files = highestAVersion.getNodes(); files.hasNext();) {
137 Node file = files.nextNode();
138 if (file.isNodeType(SlcTypes.SLC_BUNDLE_ARTIFACT)) {
139 preProcessBundleArtifact(file);
140 file.getSession().save();
141 if (log.isDebugEnabled())
142 log.debug("Pre-processed " + file.getName());
143 }
144
145 }
146 }
147 }
148
149 // if version not set or empty, use the highest version
150 // useful when indexing a product maven repository where
151 // all artifacts have the same version for a given release
152 // => the version can then be left empty
153 if (version == null || version.trim().equals(""))
154 if (allArtifactsHighestVersion != null)
155 version = allArtifactsHighestVersion.getProperty(SLC_ARTIFACT_VERSION).getString();
156 else
157 version = "0.0";
158 // throw new SlcException("Group version " + version
159 // + " is empty.");
160
161 int bundleCount = symbolicNamesToNodes.size();
162 if (log.isDebugEnabled())
163 log.debug("Indexed " + bundleCount + " bundles");
164
165 int count = 1;
166 for (Node bundleNode : symbolicNamesToNodes.values()) {
167 processBundleArtifact(bundleNode);
168 bundleNode.getSession().save();
169 if (log.isDebugEnabled())
170 log.debug(count + "/" + bundleCount + " Processed " + bundleNode.getName());
171 count++;
172 }
173
174 // indexes
175 Set<Artifact> indexes = new TreeSet<Artifact>(new ArtifactIdComparator());
176 Artifact indexArtifact = writeIndex(session, RepoConstants.BINARIES_ARTIFACT_ID, binaries);
177 indexes.add(indexArtifact);
178 indexArtifact = writeIndex(session, RepoConstants.SOURCES_ARTIFACT_ID, sources);
179 indexes.add(indexArtifact);
180 // sdk
181 writeIndex(session, RepoConstants.SDK_ARTIFACT_ID, indexes);
182 if (monitor != null)
183 monitor.worked(1);
184 }
185
186 private Version extractOsgiVersion(Node artifactVersion) throws RepositoryException {
187 String rawVersion = artifactVersion.getProperty(SLC_ARTIFACT_VERSION).getString();
188 String cleanVersion = rawVersion.replace("-SNAPSHOT", ".SNAPSHOT");
189 Version osgiVersion = null;
190 // log invalid version value to enable tracking them
191 try {
192 osgiVersion = new Version(cleanVersion);
193 } catch (IllegalArgumentException e) {
194 log.error("Version string " + cleanVersion + " is invalid ");
195 String twickedVersion = twickInvalidVersion(cleanVersion);
196 osgiVersion = new Version(twickedVersion);
197 log.error("Using " + twickedVersion + " instead");
198 // throw e;
199 }
200 return osgiVersion;
201 }
202
203 private String twickInvalidVersion(String tmpVersion) {
204 String[] tokens = tmpVersion.split("\\.");
205 if (tokens.length == 3 && tokens[2].lastIndexOf("-") > 0) {
206 String newSuffix = tokens[2].replaceFirst("-", ".");
207 tmpVersion = tmpVersion.replaceFirst(tokens[2], newSuffix);
208 } else if (tokens.length > 4) {
209 // FIXME manually remove other "."
210 StringTokenizer st = new StringTokenizer(tmpVersion, ".", true);
211 StringBuilder builder = new StringBuilder();
212 // Major
213 builder.append(st.nextToken()).append(st.nextToken());
214 // Minor
215 builder.append(st.nextToken()).append(st.nextToken());
216 // Micro
217 builder.append(st.nextToken()).append(st.nextToken());
218 // Qualifier
219 builder.append(st.nextToken());
220 while (st.hasMoreTokens()) {
221 // consume delimiter
222 st.nextToken();
223 if (st.hasMoreTokens())
224 builder.append("-").append(st.nextToken());
225 }
226 tmpVersion = builder.toString();
227 }
228 return tmpVersion;
229 }
230
231 private Artifact writeIndex(Session session, String artifactId, Set<Artifact> artifacts)
232 throws RepositoryException {
233 Artifact artifact = new DefaultArtifact(groupId, artifactId, "pom", version);
234 Artifact parentArtifact = parentPomCoordinates != null ? new DefaultArtifact(parentPomCoordinates) : null;
235 String pom = MavenConventionsUtils.artifactsAsDependencyPom(artifact, artifacts, parentArtifact);
236 Node node = RepoUtils.copyBytesAsArtifact(session.getNode(artifactBasePath), artifact, pom.getBytes());
237 artifactIndexer.index(node);
238
239 // TODO factorize
240 String pomSha = JcrUtils.checksumFile(node, "SHA-1");
241 JcrUtils.copyBytesAsFile(node.getParent(), node.getName() + ".sha1", pomSha.getBytes());
242 String pomMd5 = JcrUtils.checksumFile(node, "MD5");
243 JcrUtils.copyBytesAsFile(node.getParent(), node.getName() + ".md5", pomMd5.getBytes());
244 session.save();
245 return artifact;
246 }
247
248 protected void preProcessBundleArtifact(Node bundleNode) throws RepositoryException {
249
250 String symbolicName = JcrUtils.get(bundleNode, SLC_SYMBOLIC_NAME);
251 if (symbolicName.endsWith(".source")) {
252 // TODO make a shared node with classifier 'sources'?
253 String bundleName = RepoUtils.extractBundleNameFromSourceName(symbolicName);
254 for (String excludedSuffix : excludedSuffixes) {
255 if (bundleName.endsWith(excludedSuffix))
256 return;// skip adding to sources
257 }
258 sources.add(RepoUtils.asArtifact(bundleNode));
259 return;
260 }
261
262 NodeIterator exportPackages = bundleNode.getNodes(SLC_ + Constants.EXPORT_PACKAGE);
263 while (exportPackages.hasNext()) {
264 Node exportPackage = exportPackages.nextNode();
265 String pkg = JcrUtils.get(exportPackage, SLC_NAME);
266 packagesToSymbolicNames.put(pkg, symbolicName);
267 }
268
269 symbolicNamesToNodes.put(symbolicName, bundleNode);
270 for (String excludedSuffix : excludedSuffixes) {
271 if (symbolicName.endsWith(excludedSuffix))
272 return;// skip adding to binaries
273 }
274 binaries.add(RepoUtils.asArtifact(bundleNode));
275
276 if (bundleNode.getSession().hasPendingChanges())
277 bundleNode.getSession().save();
278 }
279
280 protected void processBundleArtifact(Node bundleNode) throws RepositoryException {
281 Node artifactFolder = bundleNode.getParent();
282 String baseName = FilenameUtils.getBaseName(bundleNode.getName());
283
284 // pom
285 String pomName = baseName + ".pom";
286 if (artifactFolder.hasNode(pomName) && !overridePoms)
287 return;// skip
288
289 String pom = generatePomForBundle(bundleNode);
290 Node pomNode = JcrUtils.copyBytesAsFile(artifactFolder, pomName, pom.getBytes());
291 // checksum
292 String bundleSha = JcrUtils.checksumFile(bundleNode, "SHA-1");
293 JcrUtils.copyBytesAsFile(artifactFolder, bundleNode.getName() + ".sha1", bundleSha.getBytes());
294 String pomSha = JcrUtils.checksumFile(pomNode, "SHA-1");
295 JcrUtils.copyBytesAsFile(artifactFolder, pomNode.getName() + ".sha1", pomSha.getBytes());
296 }
297
298 private String generatePomForBundle(Node n) throws RepositoryException {
299 String ownSymbolicName = JcrUtils.get(n, SLC_SYMBOLIC_NAME);
300
301 StringBuffer p = new StringBuffer();
302
303 // XML header
304 p.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
305 p.append(
306 "<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");
307 p.append("<modelVersion>4.0.0</modelVersion>");
308
309 // Artifact
310 p.append("<groupId>").append(JcrUtils.get(n, SLC_GROUP_ID)).append("</groupId>\n");
311 p.append("<artifactId>").append(JcrUtils.get(n, SLC_ARTIFACT_ID)).append("</artifactId>\n");
312 p.append("<version>").append(JcrUtils.get(n, SLC_ARTIFACT_VERSION)).append("</version>\n");
313 p.append("<packaging>pom</packaging>\n");
314 if (n.hasProperty(SLC_ + Constants.BUNDLE_NAME))
315 p.append("<name>").append(JcrUtils.get(n, SLC_ + Constants.BUNDLE_NAME)).append("</name>\n");
316 if (n.hasProperty(SLC_ + Constants.BUNDLE_DESCRIPTION))
317 p.append("<description>").append(JcrUtils.get(n, SLC_ + Constants.BUNDLE_DESCRIPTION))
318 .append("</description>\n");
319
320 // Dependencies
321 Set<String> dependenciesSymbolicNames = new TreeSet<String>();
322 Set<String> optionalSymbolicNames = new TreeSet<String>();
323 NodeIterator importPackages = n.getNodes(SLC_ + Constants.IMPORT_PACKAGE);
324 while (importPackages.hasNext()) {
325 Node importPackage = importPackages.nextNode();
326 String pkg = JcrUtils.get(importPackage, SLC_NAME);
327 if (packagesToSymbolicNames.containsKey(pkg)) {
328 String dependencySymbolicName = packagesToSymbolicNames.get(pkg);
329 if (JcrUtils.check(importPackage, SLC_OPTIONAL))
330 optionalSymbolicNames.add(dependencySymbolicName);
331 else
332 dependenciesSymbolicNames.add(dependencySymbolicName);
333 } else {
334 if (!JcrUtils.check(importPackage, SLC_OPTIONAL) && !systemPackages.contains(pkg))
335 log.warn("No bundle found for pkg " + pkg);
336 }
337 }
338
339 if (n.hasNode(SLC_ + Constants.FRAGMENT_HOST)) {
340 String fragmentHost = JcrUtils.get(n.getNode(SLC_ + Constants.FRAGMENT_HOST), SLC_SYMBOLIC_NAME);
341 dependenciesSymbolicNames.add(fragmentHost);
342 }
343
344 // TODO require bundles
345
346 List<Node> dependencyNodes = new ArrayList<Node>();
347 for (String depSymbName : dependenciesSymbolicNames) {
348 if (depSymbName.equals(ownSymbolicName))
349 continue;// skip self
350
351 if (symbolicNamesToNodes.containsKey(depSymbName))
352 dependencyNodes.add(symbolicNamesToNodes.get(depSymbName));
353 else
354 log.warn("Could not find node for " + depSymbName);
355 }
356 List<Node> optionalDependencyNodes = new ArrayList<Node>();
357 for (String depSymbName : optionalSymbolicNames) {
358 if (symbolicNamesToNodes.containsKey(depSymbName))
359 optionalDependencyNodes.add(symbolicNamesToNodes.get(depSymbName));
360 else
361 log.warn("Could not find node for " + depSymbName);
362 }
363
364 p.append("<dependencies>\n");
365 for (Node dependencyNode : dependencyNodes) {
366 p.append("<dependency>\n");
367 p.append("\t<groupId>").append(JcrUtils.get(dependencyNode, SLC_GROUP_ID)).append("</groupId>\n");
368 p.append("\t<artifactId>").append(JcrUtils.get(dependencyNode, SLC_ARTIFACT_ID)).append("</artifactId>\n");
369 p.append("</dependency>\n");
370 }
371
372 if (optionalDependencyNodes.size() > 0)
373 p.append("<!-- OPTIONAL -->\n");
374 for (Node dependencyNode : optionalDependencyNodes) {
375 p.append("<dependency>\n");
376 p.append("\t<groupId>").append(JcrUtils.get(dependencyNode, SLC_GROUP_ID)).append("</groupId>\n");
377 p.append("\t<artifactId>").append(JcrUtils.get(dependencyNode, SLC_ARTIFACT_ID)).append("</artifactId>\n");
378 p.append("\t<optional>true</optional>\n");
379 p.append("</dependency>\n");
380 }
381 p.append("</dependencies>\n");
382
383 // Dependency management
384 p.append("<dependencyManagement>\n");
385 p.append("<dependencies>\n");
386 p.append("<dependency>\n");
387 p.append("\t<groupId>").append(groupId).append("</groupId>\n");
388 p.append("\t<artifactId>").append(ownSymbolicName.endsWith(".source") ? RepoConstants.SOURCES_ARTIFACT_ID
389 : RepoConstants.BINARIES_ARTIFACT_ID).append("</artifactId>\n");
390 p.append("\t<version>").append(version).append("</version>\n");
391 p.append("\t<type>pom</type>\n");
392 p.append("\t<scope>import</scope>\n");
393 p.append("</dependency>\n");
394 p.append("</dependencies>\n");
395 p.append("</dependencyManagement>\n");
396
397 p.append("</project>\n");
398 return p.toString();
399 }
400
401 /* DEPENDENCY INJECTION */
402 public void setRepository(Repository repository) {
403 this.repository = repository;
404 }
405
406 public void setWorkspace(String workspace) {
407 this.workspace = workspace;
408 }
409
410 public void setGroupId(String groupId) {
411 this.groupId = groupId;
412 }
413
414 public void setParentPomCoordinates(String parentPomCoordinates) {
415 this.parentPomCoordinates = parentPomCoordinates;
416 }
417
418 public void setArtifactBasePath(String artifactBasePath) {
419 this.artifactBasePath = artifactBasePath;
420 }
421
422 public void setVersion(String version) {
423 this.version = version;
424 }
425
426 public void setExcludedSuffixes(List<String> excludedSuffixes) {
427 this.excludedSuffixes = excludedSuffixes;
428 }
429
430 public void setOverridePoms(Boolean overridePoms) {
431 this.overridePoms = overridePoms;
432 }
433
434 public void setArtifactIndexer(ArtifactIndexer artifactIndexer) {
435 this.artifactIndexer = artifactIndexer;
436 }
437 }