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