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