]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.repo/src/main/java/org/argeo/slc/repo/ModularDistributionFactory.java
Improve licenses and sources
[gpl/argeo-slc.git] / runtime / org.argeo.slc.repo / src / main / java / org / argeo / slc / repo / ModularDistributionFactory.java
1 package org.argeo.slc.repo;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.ByteArrayOutputStream;
5 import java.io.File;
6 import java.io.FileWriter;
7 import java.io.IOException;
8 import java.io.Writer;
9 import java.util.Iterator;
10 import java.util.jar.Attributes;
11 import java.util.jar.JarEntry;
12 import java.util.jar.JarOutputStream;
13 import java.util.jar.Manifest;
14
15 import javax.jcr.Node;
16 import javax.jcr.RepositoryException;
17 import javax.jcr.Session;
18 import javax.jcr.nodetype.NodeType;
19
20 import org.apache.commons.io.FileUtils;
21 import org.apache.commons.io.IOUtils;
22 import org.argeo.jcr.JcrUtils;
23 import org.argeo.slc.CategorizedNameVersion;
24 import org.argeo.slc.NameVersion;
25 import org.argeo.slc.SlcException;
26 import org.argeo.slc.jcr.SlcNames;
27 import org.argeo.slc.jcr.SlcTypes;
28 import org.osgi.framework.Constants;
29 import org.sonatype.aether.artifact.Artifact;
30 import org.sonatype.aether.util.artifact.DefaultArtifact;
31
32 /**
33 * Creates a jar bundle from an ArgeoOsgiDistribution. This jar is then
34 * persisted and indexed in a java repository.
35 *
36 * It does the following <list><li>Creates a Manifest</li><li>Creates files
37 * indexes (csv, feature.xml ...)</li><li>Populate the corresponding jar</li><li>
38 * Save it in the repository</li><li>Index the node and creates corresponding
39 * sha1 and md5 files</li> </list>
40 *
41 */
42 public class ModularDistributionFactory implements Runnable {
43
44 private Session javaSession;
45 private ArgeoOsgiDistribution osgiDistribution;
46 private String modularDistributionSeparator = ",";
47 private String artifactBasePath = RepoConstants.DEFAULT_ARTIFACTS_BASE_PATH;
48 private String artifactType = "jar";
49
50 // Constants
51 private final static String CSV_FILE_NAME = "modularDistribution.csv";
52 private final static String FEATURE_FILE_NAME = "feature.xml";
53 public static int BUFFER_SIZE = 10240;
54
55 /** Convenience constructor with minimal configuration */
56 public ModularDistributionFactory(Session javaSession,
57 ArgeoOsgiDistribution osgiDistribution) {
58 this.javaSession = javaSession;
59 this.osgiDistribution = osgiDistribution;
60 }
61
62 @Override
63 public void run() {
64 internalCreateDistribution();
65 }
66
67 private void internalCreateDistribution() {
68 byte[] distFile = null;
69 try {
70 if (artifactType == "jar")
71 distFile = generateJarFile();
72 else if (artifactType == "pom")
73 distFile = generatePomFile();
74 else
75 throw new SlcException(
76 "Unimplemented distribution artifact type: "
77 + artifactType + " for "
78 + osgiDistribution.toString());
79
80 // Save in java repository
81 Artifact osgiArtifact = new DefaultArtifact(
82 osgiDistribution.getCategory(), osgiDistribution.getName(),
83 artifactType, osgiDistribution.getVersion());
84
85 Node distNode = RepoUtils.copyBytesAsArtifact(
86 javaSession.getNode(artifactBasePath), osgiArtifact,
87 distFile);
88
89 // index
90 indexDistribution(distNode);
91
92 // Really ?
93 javaSession.save();
94 } catch (RepositoryException e) {
95 throw new SlcException(
96 "JCR error while persisting modular distribution in JCR "
97 + osgiDistribution.toString(), e);
98 }
99 }
100
101 private byte[] generateJarFile() {
102 ByteArrayOutputStream byteOut = null;
103 JarOutputStream jarOut = null;
104 try {
105 byteOut = new ByteArrayOutputStream();
106 jarOut = new JarOutputStream(byteOut, createManifest());
107 // Create various indexes
108 addToJar(createCsvDescriptor(), CSV_FILE_NAME, jarOut);
109 jarOut.close();
110 return byteOut.toByteArray();
111 } catch (IOException e) {
112 throw new SlcException(
113 "IO error while generating modular distribution "
114 + osgiDistribution.toString(), e);
115 } finally {
116 IOUtils.closeQuietly(byteOut);
117 IOUtils.closeQuietly(jarOut);
118 }
119 }
120
121 private void indexDistribution(Node distNode) throws RepositoryException {
122 distNode.addMixin(SlcTypes.SLC_MODULAR_DISTRIBUTION);
123 distNode.addMixin(SlcTypes.SLC_CATEGORIZED_NAME_VERSION);
124 distNode.setProperty(SlcNames.SLC_CATEGORY,
125 osgiDistribution.getCategory());
126 distNode.setProperty(SlcNames.SLC_NAME, osgiDistribution.getName());
127 distNode.setProperty(SlcNames.SLC_VERSION,
128 osgiDistribution.getVersion());
129
130 Node modules = JcrUtils.mkdirs(distNode, SlcNames.SLC_MODULES,
131 NodeType.NT_UNSTRUCTURED);
132
133 for (Iterator<? extends NameVersion> it = osgiDistribution
134 .nameVersions(); it.hasNext();)
135 addModule(modules, it.next());
136 }
137
138 private Manifest createManifest() {
139 Manifest manifest = new Manifest();
140
141 // TODO make this configurable
142 manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION,
143 "1.0");
144 addManifestAttribute(manifest,
145 Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, "JavaSE-1.6");
146 addManifestAttribute(manifest, Constants.BUNDLE_VENDOR, "Argeo");
147 addManifestAttribute(manifest, Constants.BUNDLE_MANIFESTVERSION, "2");
148 addManifestAttribute(manifest, "Bundle-License",
149 "http://www.apache.org/licenses/LICENSE-2.0.txt");
150
151 // TODO define a user friendly name
152 addManifestAttribute(manifest, Constants.BUNDLE_NAME,
153 osgiDistribution.getName());
154
155 // Categorized name version
156 addManifestAttribute(manifest, RepoConstants.SLC_GROUP_ID,
157 osgiDistribution.getCategory());
158 addManifestAttribute(manifest, Constants.BUNDLE_SYMBOLICNAME,
159 osgiDistribution.getName());
160 addManifestAttribute(manifest, Constants.BUNDLE_VERSION,
161 osgiDistribution.getVersion());
162
163 return manifest;
164 }
165
166 private void addManifestAttribute(Manifest manifest, String name,
167 String value) {
168 manifest.getMainAttributes().put(new Attributes.Name(name), value);
169 }
170
171 private byte[] createCsvDescriptor() {
172 Writer writer = null;
173 try {
174 // FIXME remove use of tmp file.
175 File tmpFile = File.createTempFile("modularDistribution", "csv");
176 tmpFile.deleteOnExit();
177 writer = new FileWriter(tmpFile);
178 // Populate the file
179 for (Iterator<? extends NameVersion> it = osgiDistribution
180 .nameVersions(); it.hasNext();)
181 writer.write(getCsvLine(it.next()));
182 writer.flush();
183 return FileUtils.readFileToByteArray(tmpFile);
184 } catch (Exception e) {
185 throw new SlcException(
186 "unable to create csv distribution file for "
187 + osgiDistribution.toString(), e);
188 } finally {
189 IOUtils.closeQuietly(writer);
190 }
191 }
192
193 private byte[] createFeatureDescriptor() {
194 // Directly retrieved from Argeo maven plugin
195 // Does not work due to the lack of org.codehaus.plexus/plexus-archiver
196 // third party dependency
197
198 throw new SlcException("Unimplemented method");
199
200 // // protected void writeFeatureDescriptor() throws
201 // MojoExecutionException {
202 // File featureDesc = File.createTempFile("feature", "xml");
203 // featureDesc.deleteOnExit();
204 //
205 // Writer writer = null;
206 // try {
207 // writer = new FileWriter(featureDesc);
208 // PrettyPrintXMLWriter xmlWriter = new PrettyPrintXMLWriter(writer);
209 // xmlWriter.startElement("feature");
210 // xmlWriter.addAttribute("id", project.getArtifactId());
211 // xmlWriter.addAttribute("label", project.getName());
212 //
213 // // Version
214 // String projectVersion = project.getVersion();
215 // int indexSnapshot = projectVersion.indexOf("-SNAPSHOT");
216 // if (indexSnapshot > -1)
217 // projectVersion = projectVersion.substring(0, indexSnapshot);
218 // projectVersion = projectVersion + ".qualifier";
219 //
220 // // project.
221 // xmlWriter.addAttribute("version", projectVersion);
222 //
223 // Organization organization = project.getOrganization();
224 // if (organization != null && organization.getName() != null)
225 // xmlWriter.addAttribute("provider-name", organization.getName());
226 //
227 // if (project.getDescription() != null || project.getUrl() != null) {
228 // xmlWriter.startElement("description");
229 // if (project.getUrl() != null)
230 // xmlWriter.addAttribute("url", project.getUrl());
231 // if (project.getDescription() != null)
232 // xmlWriter.writeText(project.getDescription());
233 // xmlWriter.endElement();// description
234 // }
235 //
236 // if (feature != null && feature.getCopyright() != null
237 // || (organization != null && organization.getUrl() != null)) {
238 // xmlWriter.startElement("copyright");
239 // if (organization != null && organization.getUrl() != null)
240 // xmlWriter.addAttribute("url", organization.getUrl());
241 // if (feature.getCopyright() != null)
242 // xmlWriter.writeText(feature.getCopyright());
243 // xmlWriter.endElement();// copyright
244 // }
245 //
246 // if (feature != null && feature.getUpdateSite() != null) {
247 // xmlWriter.startElement("url");
248 // xmlWriter.startElement("update");
249 // xmlWriter.addAttribute("url", feature.getUpdateSite());
250 // xmlWriter.endElement();// update
251 // xmlWriter.endElement();// url
252 // }
253 //
254 // List licenses = project.getLicenses();
255 // if (licenses.size() > 0) {
256 // // take the first one
257 // License license = (License) licenses.get(0);
258 // xmlWriter.startElement("license");
259 //
260 // if (license.getUrl() != null)
261 // xmlWriter.addAttribute("url", license.getUrl());
262 // if (license.getComments() != null)
263 // xmlWriter.writeText(license.getComments());
264 // else if (license.getName() != null)
265 // xmlWriter.writeText(license.getName());
266 // xmlWriter.endElement();// license
267 // }
268 //
269 // // deploymentRepository.pathOf(null);
270 // if (jarDirectory == null) {
271 // Set dependencies = mavenDependencyManager
272 // .getTransitiveProjectDependencies(project, remoteRepos,
273 // local);
274 // // // protected void writeFeatureDescriptor() throws
275 // MojoExecutionException {
276 // File featureDesc = File.createTempFile("feature", "xml");
277 // featureDesc.deleteOnExit();
278 //
279 // Writer writer = null;
280 // try {
281 // writer = new FileWriter(featureDesc);
282 // PrettyPrintXMLWriter xmlWriter = new PrettyPrintXMLWriter(writer);
283 // xmlWriter.startElement("feature");
284 // xmlWriter.addAttribute("id", project.getArtifactId());
285 // xmlWriter.addAttribute("label", project.getName());
286 //
287 // // Version
288 // String projectVersion = project.getVersion();
289 // int indexSnapshot = projectVersion.indexOf("-SNAPSHOT");
290 // if (indexSnapshot > -1)
291 // projectVersion = projectVersion.substring(0, indexSnapshot);
292 // projectVersion = projectVersion + ".qualifier";
293 //
294 // // project.
295 // xmlWriter.addAttribute("version", projectVersion);
296 //
297 // Organization organization = project.getOrganization();
298 // if (organization != null && organization.getName() != null)
299 // xmlWriter.addAttribute("provider-name", organization.getName());
300 //
301 // if (project.getDescription() != null || project.getUrl() != null) {
302 // xmlWriter.startElement("description");
303 // if (project.getUrl() != null)
304 // xmlWriter.addAttribute("url", project.getUrl());
305 // if (project.getDescription() != null)
306 // xmlWriter.writeText(project.getDescription());
307 // xmlWriter.endElement();// description
308 // }
309 //
310 // if (feature != null && feature.getCopyright() != null
311 // || (organization != null && organization.getUrl() != null)) {
312 // xmlWriter.startElement("copyright");
313 // if (organization != null && organization.getUrl() != null)
314 // xmlWriter.addAttribute("url", organization.getUrl());
315 // if (feature.getCopyright() != null)
316 // xmlWriter.writeText(feature.getCopyright());
317 // xmlWriter.endElement();// copyright
318 // }
319 //
320 // if (feature != null && feature.getUpdateSite() != null) {
321 // xmlWriter.startElement("url");
322 // xmlWriter.startElement("update");
323 // xmlWriter.addAttribute("url", feature.getUpdateSite());
324 // xmlWriter.endElement();// update
325 // xmlWriter.endElement();// url
326 // }
327 //
328 // List licenses = project.getLicenses();
329 // if (licenses.size() > 0) {
330 // // take the first one
331 // License license = (License) licenses.get(0);
332 // xmlWriter.startElement("license");
333 //
334 // if (license.getUrl() != null)
335 // xmlWriter.addAttribute("url", license.getUrl());
336 // if (license.getComments() != null)
337 // xmlWriter.writeText(license.getComments());
338 // else if (license.getName() != null)
339 // xmlWriter.writeText(license.getName());
340 // xmlWriter.endElement();// license
341 // }
342 //
343 // // deploymentRepository.pathOf(null);
344 // if (jarDirectory == null) {
345 // Set dependencies = mavenDependencyManager
346 // .getTransitiveProjectDependencies(project, remoteRepos,
347 // local);
348 // for (Iterator it = dependencies.iterator(); it.hasNext();) {
349 // Artifact artifact = (Artifact) it.next();
350 // writeFeaturePlugin(xmlWriter, artifact.getFile());
351 // }
352 // } else {
353 // // TODO: filter jars
354 // File[] jars = jarDirectory.listFiles();
355 // if (jars == null)
356 // throw new MojoExecutionException("No jar found in "
357 // + jarDirectory);
358 // for (int i = 0; i < jars.length; i++) {
359 // writeFeaturePlugin(xmlWriter, jars[i]);
360 // }
361 // }
362 //
363 // xmlWriter.endElement();// feature
364 //
365 // if (getLog().isDebugEnabled())
366 // getLog().debug("Wrote Eclipse feature descriptor.");
367 // } catch (Exception e) {
368 // throw new MojoExecutionException("Cannot write feature descriptor",
369 // e);
370 // } finally {
371 // IOUtil.close(writer);
372 // }for (Iterator it = dependencies.iterator(); it.hasNext();) {
373 // Artifact artifact = (Artifact) it.next();
374 // writeFeaturePlugin(xmlWriter, artifact.getFile());
375 // }
376 // } else {
377 // // TODO: filter jars
378 // File[] jars = jarDirectory.listFiles();
379 // if (jars == null)
380 // throw new MojoExecutionException("No jar found in "
381 // + jarDirectory);
382 // for (int i = 0; i < jars.length; i++) {
383 // writeFeaturePlugin(xmlWriter, jars[i]);
384 // }
385 // }
386 //
387 // xmlWriter.endElement();// feature
388 //
389 // if (getLog().isDebugEnabled())
390 // getLog().debug("Wrote Eclipse feature descriptor.");
391 // } catch (Exception e) {
392 // throw new MojoExecutionException("Cannot write feature descriptor",
393 // e);
394 // } finally {
395 // IOUtil.close(writer);
396 // }
397 }
398
399 /** Create an Aether like distribution artifact */
400 private byte[] generatePomFile() {
401 StringBuilder b = new StringBuilder();
402 // XML header
403 b.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
404 b.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");
405 b.append("<modelVersion>4.0.0</modelVersion>");
406
407 // Artifact
408 b.append("<groupId>").append(osgiDistribution.getCategory())
409 .append("</groupId>\n");
410 b.append("<artifactId>").append(osgiDistribution.getName())
411 .append("</artifactId>\n");
412 b.append("<version>").append(osgiDistribution.getVersion())
413 .append("</version>\n");
414 b.append("<packaging>pom</packaging>\n");
415 // p.append("<name>").append("Bundle Name").append("</name>\n");
416 // p.append("<description>").append("Bundle Description").append("</description>\n");
417
418 // Dependencies
419 b.append("<dependencies>\n");
420 for (Iterator<? extends NameVersion> it = osgiDistribution
421 .nameVersions(); it.hasNext();) {
422 NameVersion nameVersion = it.next();
423 if (!(nameVersion instanceof CategorizedNameVersion))
424 throw new SlcException("Unsupported type "
425 + nameVersion.getClass());
426 CategorizedNameVersion nv = (CategorizedNameVersion) nameVersion;
427 b.append(getDependencySnippet(nv, false));
428 }
429 b.append("</dependencies>\n");
430
431 // Dependency management
432 b.append("<dependencyManagement>\n");
433 b.append("<dependencies>\n");
434
435 for (Iterator<? extends NameVersion> it = osgiDistribution
436 .nameVersions(); it.hasNext();)
437 b.append(getDependencySnippet((CategorizedNameVersion) it.next(),
438 true));
439 b.append("</dependencies>\n");
440 b.append("</dependencyManagement>\n");
441
442 b.append("</project>\n");
443 return b.toString().getBytes();
444 }
445
446 private String getDependencySnippet(CategorizedNameVersion cnv,
447 boolean includeVersion) { // , String type, String scope
448 StringBuilder b = new StringBuilder();
449 b.append("<dependency>\n");
450 b.append("\t<groupId>").append(cnv.getCategory())
451 .append("</groupId>\n");
452 b.append("\t<artifactId>").append(cnv.getName())
453 .append("</artifactId>\n");
454 if (includeVersion)
455 b.append("\t<version>").append(cnv.getVersion())
456 .append("</version>\n");
457 // if (type!= null)
458 // p.append("\t<type>").append(type).append("</type>\n");
459 // if (type!= null)
460 // p.append("\t<scope>").append(scope).append("</scope>\n");
461 b.append("</dependency>\n");
462 return b.toString();
463 }
464
465 // Helpers
466 private Node addModule(Node modules, NameVersion nameVersion)
467 throws RepositoryException {
468 CategorizedNameVersion cnv = (CategorizedNameVersion) nameVersion;
469 Node moduleCoord = null;
470 // TODO solve the same name issue
471 // if (modules.hasNode(cnv.getName()))
472 // moduleCoord = modules.getNode(cnv.getName());
473 // else {
474 moduleCoord = modules.addNode(cnv.getName(),
475 SlcTypes.SLC_MODULE_COORDINATES);
476 moduleCoord.setProperty(SlcNames.SLC_CATEGORY, cnv.getCategory());
477 moduleCoord.setProperty(SlcNames.SLC_NAME, cnv.getName());
478 moduleCoord.setProperty(SlcNames.SLC_VERSION, cnv.getVersion());
479 // }
480 return moduleCoord;
481 }
482
483 private void addToJar(byte[] content, String name, JarOutputStream target)
484 throws IOException {
485 ByteArrayInputStream in = null;
486 try {
487 target.putNextEntry(new JarEntry(name));
488 in = new ByteArrayInputStream(content);
489 byte[] buffer = new byte[1024];
490 while (true) {
491 int count = in.read(buffer);
492 if (count == -1)
493 break;
494 target.write(buffer, 0, count);
495 }
496 target.closeEntry();
497 } finally {
498 IOUtils.closeQuietly(in);
499 }
500 }
501
502 private String getCsvLine(NameVersion nameVersion)
503 throws RepositoryException {
504 if (!(nameVersion instanceof CategorizedNameVersion))
505 throw new SlcException("Unsupported type " + nameVersion.getClass());
506 CategorizedNameVersion cnv = (CategorizedNameVersion) nameVersion;
507 StringBuilder builder = new StringBuilder();
508
509 builder.append(cnv.getName());
510 builder.append(modularDistributionSeparator);
511 builder.append(nameVersion.getVersion());
512 builder.append(modularDistributionSeparator);
513 builder.append(cnv.getCategory().replace('.', '/'));
514 // MavenConventionsUtils.groupPath("", cnv.getCategory());
515 builder.append('/');
516 builder.append(cnv.getName());
517 builder.append('/');
518 builder.append(cnv.getVersion());
519 builder.append('/');
520 builder.append(cnv.getName());
521 builder.append('-');
522 builder.append(cnv.getVersion());
523 builder.append('.');
524 // TODO make this dynamic
525 builder.append("jar");
526 builder.append("\n");
527
528 return builder.toString();
529 }
530
531 public void setJavaSession(Session javaSession) {
532 this.javaSession = javaSession;
533 }
534
535 public void setOsgiDistribution(ArgeoOsgiDistribution osgiDistribution) {
536 this.osgiDistribution = osgiDistribution;
537 }
538
539 public void setModularDistributionSeparator(
540 String modularDistributionSeparator) {
541 this.modularDistributionSeparator = modularDistributionSeparator;
542 }
543
544 public void setArtifactBasePath(String artifactBasePath) {
545 this.artifactBasePath = artifactBasePath;
546 }
547
548 public void setArtifactType(String artifactType) {
549 this.artifactType = artifactType;
550 }
551 }