]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/osgi/NormalizeGroup.java
0d734335bce506fc0aff79ff2e295efd41287a6b
[gpl/argeo-slc.git] / runtime / org.argeo.slc.repo / src / main / java / org / argeo / slc / repo / osgi / NormalizeGroup.java
1 /*
2 * Copyright (C) 2007-2012 Argeo GmbH
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16 package org.argeo.slc.repo.osgi;
17
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.TreeSet;
24
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;
30
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.JarFileIndexer;
42 import org.argeo.slc.repo.RepoUtils;
43 import org.argeo.slc.repo.maven.MavenConventionsUtils;
44 import org.osgi.framework.Constants;
45 import org.sonatype.aether.artifact.Artifact;
46 import org.sonatype.aether.util.artifact.DefaultArtifact;
47
48 /**
49 * Make sure that all JCR metadata and Maven metadata are consistent for this
50 * group of OSGi bundles.
51 */
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";
56
57 private final static Log log = LogFactory.getLog(NormalizeGroup.class);
58
59 private Repository repository;
60 private String workspace;
61 private String groupId;
62 private String artifactBasePath = "/";
63 private String version = null;
64 private String parentPomCoordinates;
65
66 private List<String> excludedSuffixes = new ArrayList<String>();
67
68 private ArtifactIndexer artifactIndexer = new ArtifactIndexer();
69 private JarFileIndexer jarFileIndexer = new JarFileIndexer();
70
71 private List<String> systemPackages = OsgiProfile.PROFILE_JAVA_SE_1_6
72 .getSystemPackages();
73
74 // indexes
75 private Map<String, String> packagesToSymbolicNames = new HashMap<String, String>();
76 private Map<String, Node> symbolicNamesToNodes = new HashMap<String, Node>();
77
78 private Set<Artifact> binaries = new TreeSet<Artifact>(
79 new ArtifactIdComparator());
80 private Set<Artifact> sources = new TreeSet<Artifact>(
81 new ArtifactIdComparator());
82
83 public void run() {
84 Session session = null;
85 try {
86 session = repository.login(workspace);
87 Node groupNode = session.getNode(MavenConventionsUtils.groupPath(
88 artifactBasePath, groupId));
89 processGroupNode(groupNode, null);
90 } catch (Exception e) {
91 throw new SlcException("Cannot normalize group " + groupId + " in "
92 + workspace, e);
93 } finally {
94 JcrUtils.logoutQuietly(session);
95 }
96 }
97
98 public synchronized void processGroupNode(Node groupNode, String version,
99 ArgeoMonitor monitor) throws RepositoryException {
100 // FIXME better encapsulate
101 groupId = groupNode.getProperty(SlcNames.SLC_GROUP_BASE_ID).getString();
102 this.version = version;
103 processGroupNode(groupNode, monitor);
104 }
105
106 protected void processGroupNode(Node groupNode, ArgeoMonitor monitor)
107 throws RepositoryException {
108 if (monitor != null)
109 monitor.subTask("Group " + groupId);
110 Session session = groupNode.getSession();
111 for (NodeIterator artifactBases = groupNode.getNodes(); artifactBases
112 .hasNext();) {
113 Node artifactBase = artifactBases.nextNode();
114 if (artifactBase.isNodeType(SlcTypes.SLC_ARTIFACT_BASE)) {
115 for (NodeIterator artifactVersions = artifactBase.getNodes(); artifactVersions
116 .hasNext();) {
117 Node artifactVersion = artifactVersions.nextNode();
118 if (artifactVersion
119 .isNodeType(SlcTypes.SLC_ARTIFACT_VERSION_BASE))
120 for (NodeIterator files = artifactVersion.getNodes(); files
121 .hasNext();) {
122 Node file = files.nextNode();
123 if (file.isNodeType(SlcTypes.SLC_BUNDLE_ARTIFACT)) {
124 preProcessBundleArtifact(file);
125 file.getSession().save();
126 if (log.isDebugEnabled())
127 log.debug("Pre-processed " + file.getName());
128 }
129
130 }
131 }
132 }
133 }
134 // NodeIterator bundlesIt = listBundleArtifacts(session);
135 //
136 // while (bundlesIt.hasNext()) {
137 // Node bundleNode = bundlesIt.nextNode();
138 // preProcessBundleArtifact(bundleNode);
139 // bundleNode.getSession().save();
140 // if (log.isDebugEnabled())
141 // log.debug("Pre-processed " + bundleNode.getName());
142 // }
143
144 int bundleCount = symbolicNamesToNodes.size();
145 if (log.isDebugEnabled())
146 log.debug("Indexed " + bundleCount + " bundles");
147
148 int count = 1;
149 for (Node bundleNode : symbolicNamesToNodes.values()) {
150 processBundleArtifact(bundleNode);
151 bundleNode.getSession().save();
152 if (log.isDebugEnabled())
153 log.debug(count + "/" + bundleCount + " Processed "
154 + bundleNode.getName());
155 count++;
156 }
157
158 // indexes
159 Set<Artifact> indexes = new TreeSet<Artifact>(
160 new ArtifactIdComparator());
161 Artifact indexArtifact = writeIndex(session, BINARIES_ARTIFACT_ID,
162 binaries);
163 indexes.add(indexArtifact);
164 indexArtifact = writeIndex(session, SOURCES_ARTIFACT_ID, sources);
165 indexes.add(indexArtifact);
166 // sdk
167 writeIndex(session, SDK_ARTIFACT_ID, indexes);
168 if (monitor != null)
169 monitor.worked(1);
170 }
171
172 private Artifact writeIndex(Session session, String artifactId,
173 Set<Artifact> artifacts) throws RepositoryException {
174 Artifact artifact = new DefaultArtifact(groupId, artifactId, "pom",
175 version);
176 Artifact parentArtifact = parentPomCoordinates != null ? new DefaultArtifact(
177 parentPomCoordinates) : null;
178 String pom = MavenConventionsUtils.artifactsAsDependencyPom(artifact,
179 artifacts, parentArtifact);
180 Node node = RepoUtils.copyBytesAsArtifact(
181 session.getNode(artifactBasePath), artifact, pom.getBytes());
182 artifactIndexer.index(node);
183
184 // FIXME factorize
185 String pomSha = JcrUtils.checksumFile(node, "SHA-1");
186 JcrUtils.copyBytesAsFile(node.getParent(), node.getName() + ".sha1",
187 pomSha.getBytes());
188 session.save();
189 return artifact;
190 }
191
192 protected void preProcessBundleArtifact(Node bundleNode)
193 throws RepositoryException {
194 artifactIndexer.index(bundleNode);
195 jarFileIndexer.index(bundleNode);
196
197 String symbolicName = JcrUtils.get(bundleNode, SLC_SYMBOLIC_NAME);
198
199 if (symbolicName.endsWith(".source")) {
200 // TODO make a shared node with classifier 'sources'
201 String bundleName = RepoUtils
202 .extractBundleNameFromSourceName(symbolicName);
203 for (String excludedSuffix : excludedSuffixes) {
204 if (bundleName.endsWith(excludedSuffix))
205 return;// skip adding to sources
206 }
207 sources.add(RepoUtils.asArtifact(bundleNode));
208 return;
209 }
210
211 NodeIterator exportPackages = bundleNode.getNodes(SLC_
212 + Constants.EXPORT_PACKAGE);
213 while (exportPackages.hasNext()) {
214 Node exportPackage = exportPackages.nextNode();
215 String pkg = JcrUtils.get(exportPackage, SLC_NAME);
216 packagesToSymbolicNames.put(pkg, symbolicName);
217 }
218
219 symbolicNamesToNodes.put(symbolicName, bundleNode);
220 for (String excludedSuffix : excludedSuffixes) {
221 if (symbolicName.endsWith(excludedSuffix))
222 return;// skip adding to binaries
223 }
224 binaries.add(RepoUtils.asArtifact(bundleNode));
225
226 if (bundleNode.getSession().hasPendingChanges())
227 bundleNode.getSession().save();
228 }
229
230 protected void processBundleArtifact(Node bundleNode)
231 throws RepositoryException {
232 Node artifactFolder = bundleNode.getParent();
233 String baseName = FilenameUtils.getBaseName(bundleNode.getName());
234
235 // pom
236 String pom = generatePomForBundle(bundleNode);
237 String pomName = baseName + ".pom";
238 Node pomNode = JcrUtils.copyBytesAsFile(artifactFolder, pomName,
239 pom.getBytes());
240
241 // checksum
242 String bundleSha = JcrUtils.checksumFile(bundleNode, "SHA-1");
243 JcrUtils.copyBytesAsFile(artifactFolder,
244 bundleNode.getName() + ".sha1", bundleSha.getBytes());
245 String pomSha = JcrUtils.checksumFile(pomNode, "SHA-1");
246 JcrUtils.copyBytesAsFile(artifactFolder, pomNode.getName() + ".sha1",
247 pomSha.getBytes());
248 }
249
250 private String generatePomForBundle(Node n) throws RepositoryException {
251 String ownSymbolicName = JcrUtils.get(n, SLC_SYMBOLIC_NAME);
252
253 StringBuffer p = new StringBuffer();
254
255 // XML header
256 p.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
257 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");
258 p.append("<modelVersion>4.0.0</modelVersion>");
259
260 // Artifact
261 // p.append("<parent><groupId>org.argeo</groupId><artifactId>parent</artifactId><version>1.2.0</version></parent>\n");
262 p.append("<groupId>").append(JcrUtils.get(n, SLC_GROUP_ID))
263 .append("</groupId>\n");
264 p.append("<artifactId>").append(JcrUtils.get(n, SLC_ARTIFACT_ID))
265 .append("</artifactId>\n");
266 p.append("<version>").append(JcrUtils.get(n, SLC_ARTIFACT_VERSION))
267 .append("</version>\n");
268 p.append("<packaging>pom</packaging>\n");
269 if (n.hasProperty(SLC_ + Constants.BUNDLE_NAME))
270 p.append("<name>")
271 .append(JcrUtils.get(n, SLC_ + Constants.BUNDLE_NAME))
272 .append("</name>\n");
273 if (n.hasProperty(SLC_ + Constants.BUNDLE_DESCRIPTION))
274 p.append("<description>")
275 .append(JcrUtils
276 .get(n, SLC_ + Constants.BUNDLE_DESCRIPTION))
277 .append("</description>\n");
278
279 // Dependencies
280 Set<String> dependenciesSymbolicNames = new TreeSet<String>();
281 Set<String> optionalSymbolicNames = new TreeSet<String>();
282 NodeIterator importPackages = n.getNodes(SLC_
283 + Constants.IMPORT_PACKAGE);
284 while (importPackages.hasNext()) {
285 Node importPackage = importPackages.nextNode();
286 String pkg = JcrUtils.get(importPackage, SLC_NAME);
287 if (packagesToSymbolicNames.containsKey(pkg)) {
288 String dependencySymbolicName = packagesToSymbolicNames
289 .get(pkg);
290 if (JcrUtils.check(importPackage, SLC_OPTIONAL))
291 optionalSymbolicNames.add(dependencySymbolicName);
292 else
293 dependenciesSymbolicNames.add(dependencySymbolicName);
294 } else {
295 if (!JcrUtils.check(importPackage, SLC_OPTIONAL)
296 && !systemPackages.contains(pkg))
297 log.warn("No bundle found for pkg " + pkg);
298 }
299 }
300
301 if (n.hasNode(SLC_ + Constants.FRAGMENT_HOST)) {
302 String fragmentHost = JcrUtils.get(
303 n.getNode(SLC_ + Constants.FRAGMENT_HOST),
304 SLC_SYMBOLIC_NAME);
305 dependenciesSymbolicNames.add(fragmentHost);
306 }
307
308 // TODO require bundles
309
310 List<Node> dependencyNodes = new ArrayList<Node>();
311 for (String depSymbName : dependenciesSymbolicNames) {
312 if (depSymbName.equals(ownSymbolicName))
313 continue;// skip self
314
315 if (symbolicNamesToNodes.containsKey(depSymbName))
316 dependencyNodes.add(symbolicNamesToNodes.get(depSymbName));
317 else
318 log.warn("Could not find node for " + depSymbName);
319 }
320 List<Node> optionalDependencyNodes = new ArrayList<Node>();
321 for (String depSymbName : optionalSymbolicNames) {
322 if (symbolicNamesToNodes.containsKey(depSymbName))
323 optionalDependencyNodes.add(symbolicNamesToNodes
324 .get(depSymbName));
325 else
326 log.warn("Could not find node for " + depSymbName);
327 }
328
329 p.append("<dependencies>\n");
330 for (Node dependencyNode : dependencyNodes) {
331 p.append("<dependency>\n");
332 p.append("\t<groupId>")
333 .append(JcrUtils.get(dependencyNode, SLC_GROUP_ID))
334 .append("</groupId>\n");
335 p.append("\t<artifactId>")
336 .append(JcrUtils.get(dependencyNode, SLC_ARTIFACT_ID))
337 .append("</artifactId>\n");
338 p.append("</dependency>\n");
339 }
340
341 if (optionalDependencyNodes.size() > 0)
342 p.append("<!-- OPTIONAL -->\n");
343 for (Node dependencyNode : optionalDependencyNodes) {
344 p.append("<dependency>\n");
345 p.append("\t<groupId>")
346 .append(JcrUtils.get(dependencyNode, SLC_GROUP_ID))
347 .append("</groupId>\n");
348 p.append("\t<artifactId>")
349 .append(JcrUtils.get(dependencyNode, SLC_ARTIFACT_ID))
350 .append("</artifactId>\n");
351 p.append("\t<optional>true</optional>\n");
352 p.append("</dependency>\n");
353 }
354 p.append("</dependencies>\n");
355
356 // Dependency management
357 p.append("<dependencyManagement>\n");
358 p.append("<dependencies>\n");
359 p.append("<dependency>\n");
360 p.append("\t<groupId>").append(groupId).append("</groupId>\n");
361 p.append("\t<artifactId>")
362 .append(ownSymbolicName.endsWith(".source") ? SOURCES_ARTIFACT_ID
363 : BINARIES_ARTIFACT_ID).append("</artifactId>\n");
364 p.append("\t<version>").append(version).append("</version>\n");
365 p.append("\t<type>pom</type>\n");
366 p.append("\t<scope>import</scope>\n");
367 p.append("</dependency>\n");
368 p.append("</dependencies>\n");
369 p.append("</dependencyManagement>\n");
370
371 p.append("</project>\n");
372 return p.toString();
373 }
374
375 public void setRepository(Repository repository) {
376 this.repository = repository;
377 }
378
379 public void setWorkspace(String workspace) {
380 this.workspace = workspace;
381 }
382
383 public void setGroupId(String groupId) {
384 this.groupId = groupId;
385 }
386
387 public void setParentPomCoordinates(String parentPomCoordinates) {
388 this.parentPomCoordinates = parentPomCoordinates;
389 }
390
391 public void setArtifactBasePath(String artifactBasePath) {
392 this.artifactBasePath = artifactBasePath;
393 }
394
395 public void setVersion(String version) {
396 this.version = version;
397 }
398
399 public void setExcludedSuffixes(List<String> excludedSuffixes) {
400 this.excludedSuffixes = excludedSuffixes;
401 }
402
403 }