1 package org
.argeo
.slc
.repo
;
3 import java
.io
.ByteArrayInputStream
;
4 import java
.io
.ByteArrayOutputStream
;
6 import java
.io
.FileWriter
;
7 import java
.io
.IOException
;
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
;
15 import javax
.jcr
.Node
;
16 import javax
.jcr
.RepositoryException
;
17 import javax
.jcr
.Session
;
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
;
30 * Creates a jar bundle from an ArgeoOsgiDistribution. This jar is then
31 * persisted and indexed in a java repository using the OSGI Factory.
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>
39 public class ModularDistributionFactory
implements Runnable
{
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";
49 private final static String CSV_FILE_NAME
= "modularDistribution.csv";
51 // private final static String FEATURE_FILE_NAME = "feature.xml";
52 // private static int BUFFER_SIZE = 10240;
54 /** Convenience constructor with minimal configuration */
55 public ModularDistributionFactory(OsgiFactory osgiFactory
,
56 ArgeoOsgiDistribution osgiDistribution
) {
57 this.osgiFactory
= osgiFactory
;
58 this.osgiDistribution
= osgiDistribution
;
63 byte[] distFile
= null;
65 javaSession
= osgiFactory
.openJavaSession();
67 if (artifactType
== "jar")
68 distFile
= generateJarFile();
69 else if (artifactType
== "pom")
70 distFile
= generatePomFile();
72 throw new SlcException(
73 "Unimplemented distribution artifact type: "
74 + artifactType
+ " for "
75 + osgiDistribution
.toString());
77 // Save in java repository
78 Artifact osgiArtifact
= new DefaultArtifact(
79 osgiDistribution
.getCategory(), osgiDistribution
.getName(),
80 artifactType
, osgiDistribution
.getVersion());
82 Node distNode
= RepoUtils
.copyBytesAsArtifact(
83 javaSession
.getNode(artifactBasePath
), osgiArtifact
,
87 osgiFactory
.indexNode(distNode
);
89 // We use a specific session. Save before closing
91 } catch (RepositoryException e
) {
92 throw new SlcException(
93 "JCR error while persisting modular distribution in JCR "
94 + osgiDistribution
.toString(), e
);
96 JcrUtils
.logoutQuietly(javaSession
);
100 private byte[] generateJarFile() {
101 ByteArrayOutputStream byteOut
= null;
102 JarOutputStream jarOut
= null;
104 byteOut
= new ByteArrayOutputStream();
105 jarOut
= new JarOutputStream(byteOut
, createManifest());
106 // Create various indexes
107 addToJar(createCsvDescriptor(), CSV_FILE_NAME
, jarOut
);
109 return byteOut
.toByteArray();
110 } catch (IOException e
) {
111 throw new SlcException(
112 "IO error while generating modular distribution "
113 + osgiDistribution
.toString(), e
);
115 IOUtils
.closeQuietly(byteOut
);
116 IOUtils
.closeQuietly(jarOut
);
120 // 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());
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);
135 // for (Iterator<? extends NameVersion> it = osgiDistribution
136 // .nameVersions(); it.hasNext();)
137 // addModule(modules, it.next());
140 private Manifest
createManifest() {
141 Manifest manifest
= new Manifest();
143 // TODO make this configurable
144 manifest
.getMainAttributes().put(Attributes
.Name
.MANIFEST_VERSION
,
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");
153 // TODO define a user friendly name
154 addManifestAttribute(manifest
, Constants
.BUNDLE_NAME
,
155 osgiDistribution
.getName());
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());
168 private void addManifestAttribute(Manifest manifest
, String name
,
170 manifest
.getMainAttributes().put(new Attributes
.Name(name
), value
);
173 private byte[] createCsvDescriptor() {
174 Writer writer
= null;
176 // FIXME remove use of tmp file.
177 File tmpFile
= File
.createTempFile("modularDistribution", "csv");
178 tmpFile
.deleteOnExit();
179 writer
= new FileWriter(tmpFile
);
181 for (Iterator
<?
extends NameVersion
> it
= osgiDistribution
182 .nameVersions(); it
.hasNext();)
183 writer
.write(getCsvLine(it
.next()));
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
);
191 IOUtils
.closeQuietly(writer
);
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
201 throw new SlcException("Unimplemented method");
203 // // protected void writeFeatureDescriptor() throws
204 // MojoExecutionException {
205 // File featureDesc = File.createTempFile("feature", "xml");
206 // featureDesc.deleteOnExit();
208 // Writer writer = null;
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());
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";
224 // xmlWriter.addAttribute("version", projectVersion);
226 // Organization organization = project.getOrganization();
227 // if (organization != null && organization.getName() != null)
228 // xmlWriter.addAttribute("provider-name", organization.getName());
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
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
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
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");
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
272 // // deploymentRepository.pathOf(null);
273 // if (jarDirectory == null) {
274 // Set dependencies = mavenDependencyManager
275 // .getTransitiveProjectDependencies(project, remoteRepos,
277 // // // protected void writeFeatureDescriptor() throws
278 // MojoExecutionException {
279 // File featureDesc = File.createTempFile("feature", "xml");
280 // featureDesc.deleteOnExit();
282 // Writer writer = null;
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());
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";
298 // xmlWriter.addAttribute("version", projectVersion);
300 // Organization organization = project.getOrganization();
301 // if (organization != null && organization.getName() != null)
302 // xmlWriter.addAttribute("provider-name", organization.getName());
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
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
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
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");
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
346 // // deploymentRepository.pathOf(null);
347 // if (jarDirectory == null) {
348 // Set dependencies = mavenDependencyManager
349 // .getTransitiveProjectDependencies(project, remoteRepos,
351 // for (Iterator it = dependencies.iterator(); it.hasNext();) {
352 // Artifact artifact = (Artifact) it.next();
353 // writeFeaturePlugin(xmlWriter, artifact.getFile());
356 // // TODO: filter jars
357 // File[] jars = jarDirectory.listFiles();
359 // throw new MojoExecutionException("No jar found in "
361 // for (int i = 0; i < jars.length; i++) {
362 // writeFeaturePlugin(xmlWriter, jars[i]);
366 // xmlWriter.endElement();// feature
368 // if (getLog().isDebugEnabled())
369 // getLog().debug("Wrote Eclipse feature descriptor.");
370 // } catch (Exception e) {
371 // throw new MojoExecutionException("Cannot write feature descriptor",
374 // IOUtil.close(writer);
375 // }for (Iterator it = dependencies.iterator(); it.hasNext();) {
376 // Artifact artifact = (Artifact) it.next();
377 // writeFeaturePlugin(xmlWriter, artifact.getFile());
380 // // TODO: filter jars
381 // File[] jars = jarDirectory.listFiles();
383 // throw new MojoExecutionException("No jar found in "
385 // for (int i = 0; i < jars.length; i++) {
386 // writeFeaturePlugin(xmlWriter, jars[i]);
390 // xmlWriter.endElement();// feature
392 // if (getLog().isDebugEnabled())
393 // getLog().debug("Wrote Eclipse feature descriptor.");
394 // } catch (Exception e) {
395 // throw new MojoExecutionException("Cannot write feature descriptor",
398 // IOUtil.close(writer);
402 /** Create an Aether like distribution artifact */
403 private byte[] generatePomFile() {
404 StringBuilder b
= new StringBuilder();
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>");
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");
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));
432 b
.append("</dependencies>\n");
434 // Dependency management
435 b
.append("<dependencyManagement>\n");
436 b
.append("<dependencies>\n");
438 for (Iterator
<?
extends NameVersion
> it
= osgiDistribution
439 .nameVersions(); it
.hasNext();)
440 b
.append(getDependencySnippet((CategorizedNameVersion
) it
.next(),
442 b
.append("</dependencies>\n");
443 b
.append("</dependencyManagement>\n");
445 b
.append("</project>\n");
446 return b
.toString().getBytes();
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");
458 b
.append("\t<version>").append(cnv
.getVersion())
459 .append("</version>\n");
461 // p.append("\t<type>").append(type).append("</type>\n");
463 // p.append("\t<scope>").append(scope).append("</scope>\n");
464 b
.append("</dependency>\n");
469 private void addToJar(byte[] content
, String name
, JarOutputStream target
)
471 ByteArrayInputStream in
= null;
473 target
.putNextEntry(new JarEntry(name
));
474 in
= new ByteArrayInputStream(content
);
475 byte[] buffer
= new byte[1024];
477 int count
= in
.read(buffer
);
480 target
.write(buffer
, 0, count
);
484 IOUtils
.closeQuietly(in
);
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();
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());
502 builder
.append(cnv
.getName());
504 builder
.append(cnv
.getVersion());
506 builder
.append(cnv
.getName());
508 builder
.append(cnv
.getVersion());
510 // TODO make this dynamic
511 builder
.append("jar");
512 builder
.append("\n");
514 return builder
.toString();
517 /** Enable dependency injection */
518 public void setOsgiFactory(OsgiFactory osgiFactory
) {
519 this.osgiFactory
= osgiFactory
;
522 public void setOsgiDistribution(ArgeoOsgiDistribution osgiDistribution
) {
523 this.osgiDistribution
= osgiDistribution
;
526 public void setModularDistributionSeparator(
527 String modularDistributionSeparator
) {
528 this.modularDistributionSeparator
= modularDistributionSeparator
;
531 public void setArtifactBasePath(String artifactBasePath
) {
532 this.artifactBasePath
= artifactBasePath
;
535 public void setArtifactType(String artifactType
) {
536 this.artifactType
= artifactType
;