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
;
18 import javax
.jcr
.nodetype
.NodeType
;
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
;
33 * Creates a jar bundle from an ArgeoOsgiDistribution. This jar is then
34 * persisted and indexed in a java repository.
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>
42 public class ModularDistributionFactory
implements Runnable
{
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";
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;
55 /** Convenience constructor with minimal configuration */
56 public ModularDistributionFactory(Session javaSession
,
57 ArgeoOsgiDistribution osgiDistribution
) {
58 this.javaSession
= javaSession
;
59 this.osgiDistribution
= osgiDistribution
;
64 internalCreateDistribution();
67 private void internalCreateDistribution() {
68 byte[] distFile
= null;
70 if (artifactType
== "jar")
71 distFile
= generateJarFile();
72 else if (artifactType
== "pom")
73 distFile
= generatePomFile();
75 throw new SlcException(
76 "Unimplemented distribution artifact type: "
77 + artifactType
+ " for "
78 + osgiDistribution
.toString());
80 // Save in java repository
81 Artifact osgiArtifact
= new DefaultArtifact(
82 osgiDistribution
.getCategory(), osgiDistribution
.getName(),
83 artifactType
, osgiDistribution
.getVersion());
85 Node distNode
= RepoUtils
.copyBytesAsArtifact(
86 javaSession
.getNode(artifactBasePath
), osgiArtifact
,
90 indexDistribution(distNode
);
94 } catch (RepositoryException e
) {
95 throw new SlcException(
96 "JCR error while persisting modular distribution in JCR "
97 + osgiDistribution
.toString(), e
);
101 private byte[] generateJarFile() {
102 ByteArrayOutputStream byteOut
= null;
103 JarOutputStream jarOut
= null;
105 byteOut
= new ByteArrayOutputStream();
106 jarOut
= new JarOutputStream(byteOut
, createManifest());
107 // Create various indexes
108 addToJar(createCsvDescriptor(), CSV_FILE_NAME
, jarOut
);
110 return byteOut
.toByteArray();
111 } catch (IOException e
) {
112 throw new SlcException(
113 "IO error while generating modular distribution "
114 + osgiDistribution
.toString(), e
);
116 IOUtils
.closeQuietly(byteOut
);
117 IOUtils
.closeQuietly(jarOut
);
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());
130 Node modules
= JcrUtils
.mkdirs(distNode
, SlcNames
.SLC_MODULES
,
131 NodeType
.NT_UNSTRUCTURED
);
133 for (Iterator
<?
extends NameVersion
> it
= osgiDistribution
134 .nameVersions(); it
.hasNext();)
135 addModule(modules
, it
.next());
138 private Manifest
createManifest() {
139 Manifest manifest
= new Manifest();
141 // TODO make this configurable
142 manifest
.getMainAttributes().put(Attributes
.Name
.MANIFEST_VERSION
,
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");
151 // TODO define a user friendly name
152 addManifestAttribute(manifest
, Constants
.BUNDLE_NAME
,
153 osgiDistribution
.getName());
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());
166 private void addManifestAttribute(Manifest manifest
, String name
,
168 manifest
.getMainAttributes().put(new Attributes
.Name(name
), value
);
171 private byte[] createCsvDescriptor() {
172 Writer writer
= null;
174 // FIXME remove use of tmp file.
175 File tmpFile
= File
.createTempFile("modularDistribution", "csv");
176 tmpFile
.deleteOnExit();
177 writer
= new FileWriter(tmpFile
);
179 for (Iterator
<?
extends NameVersion
> it
= osgiDistribution
180 .nameVersions(); it
.hasNext();)
181 writer
.write(getCsvLine(it
.next()));
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
);
189 IOUtils
.closeQuietly(writer
);
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
198 throw new SlcException("Unimplemented method");
200 // // protected void writeFeatureDescriptor() throws
201 // MojoExecutionException {
202 // File featureDesc = File.createTempFile("feature", "xml");
203 // featureDesc.deleteOnExit();
205 // Writer writer = null;
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());
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";
221 // xmlWriter.addAttribute("version", projectVersion);
223 // Organization organization = project.getOrganization();
224 // if (organization != null && organization.getName() != null)
225 // xmlWriter.addAttribute("provider-name", organization.getName());
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
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
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
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");
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
269 // // deploymentRepository.pathOf(null);
270 // if (jarDirectory == null) {
271 // Set dependencies = mavenDependencyManager
272 // .getTransitiveProjectDependencies(project, remoteRepos,
274 // // // protected void writeFeatureDescriptor() throws
275 // MojoExecutionException {
276 // File featureDesc = File.createTempFile("feature", "xml");
277 // featureDesc.deleteOnExit();
279 // Writer writer = null;
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());
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";
295 // xmlWriter.addAttribute("version", projectVersion);
297 // Organization organization = project.getOrganization();
298 // if (organization != null && organization.getName() != null)
299 // xmlWriter.addAttribute("provider-name", organization.getName());
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
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
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
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");
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
343 // // deploymentRepository.pathOf(null);
344 // if (jarDirectory == null) {
345 // Set dependencies = mavenDependencyManager
346 // .getTransitiveProjectDependencies(project, remoteRepos,
348 // for (Iterator it = dependencies.iterator(); it.hasNext();) {
349 // Artifact artifact = (Artifact) it.next();
350 // writeFeaturePlugin(xmlWriter, artifact.getFile());
353 // // TODO: filter jars
354 // File[] jars = jarDirectory.listFiles();
356 // throw new MojoExecutionException("No jar found in "
358 // for (int i = 0; i < jars.length; i++) {
359 // writeFeaturePlugin(xmlWriter, jars[i]);
363 // xmlWriter.endElement();// feature
365 // if (getLog().isDebugEnabled())
366 // getLog().debug("Wrote Eclipse feature descriptor.");
367 // } catch (Exception e) {
368 // throw new MojoExecutionException("Cannot write feature descriptor",
371 // IOUtil.close(writer);
372 // }for (Iterator it = dependencies.iterator(); it.hasNext();) {
373 // Artifact artifact = (Artifact) it.next();
374 // writeFeaturePlugin(xmlWriter, artifact.getFile());
377 // // TODO: filter jars
378 // File[] jars = jarDirectory.listFiles();
380 // throw new MojoExecutionException("No jar found in "
382 // for (int i = 0; i < jars.length; i++) {
383 // writeFeaturePlugin(xmlWriter, jars[i]);
387 // xmlWriter.endElement();// feature
389 // if (getLog().isDebugEnabled())
390 // getLog().debug("Wrote Eclipse feature descriptor.");
391 // } catch (Exception e) {
392 // throw new MojoExecutionException("Cannot write feature descriptor",
395 // IOUtil.close(writer);
399 /** Create an Aether like distribution artifact */
400 private byte[] generatePomFile() {
401 StringBuilder b
= new StringBuilder();
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>");
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");
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));
429 b
.append("</dependencies>\n");
431 // Dependency management
432 b
.append("<dependencyManagement>\n");
433 b
.append("<dependencies>\n");
435 for (Iterator
<?
extends NameVersion
> it
= osgiDistribution
436 .nameVersions(); it
.hasNext();)
437 b
.append(getDependencySnippet((CategorizedNameVersion
) it
.next(),
439 b
.append("</dependencies>\n");
440 b
.append("</dependencyManagement>\n");
442 b
.append("</project>\n");
443 return b
.toString().getBytes();
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");
455 b
.append("\t<version>").append(cnv
.getVersion())
456 .append("</version>\n");
458 // p.append("\t<type>").append(type).append("</type>\n");
460 // p.append("\t<scope>").append(scope).append("</scope>\n");
461 b
.append("</dependency>\n");
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());
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());
483 private void addToJar(byte[] content
, String name
, JarOutputStream target
)
485 ByteArrayInputStream in
= null;
487 target
.putNextEntry(new JarEntry(name
));
488 in
= new ByteArrayInputStream(content
);
489 byte[] buffer
= new byte[1024];
491 int count
= in
.read(buffer
);
494 target
.write(buffer
, 0, count
);
498 IOUtils
.closeQuietly(in
);
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();
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());
516 builder
.append(cnv
.getName());
518 builder
.append(cnv
.getVersion());
520 builder
.append(cnv
.getName());
522 builder
.append(cnv
.getVersion());
524 // TODO make this dynamic
525 builder
.append("jar");
526 builder
.append("\n");
528 return builder
.toString();
531 public void setJavaSession(Session javaSession
) {
532 this.javaSession
= javaSession
;
535 public void setOsgiDistribution(ArgeoOsgiDistribution osgiDistribution
) {
536 this.osgiDistribution
= osgiDistribution
;
539 public void setModularDistributionSeparator(
540 String modularDistributionSeparator
) {
541 this.modularDistributionSeparator
= modularDistributionSeparator
;
544 public void setArtifactBasePath(String artifactBasePath
) {
545 this.artifactBasePath
= artifactBasePath
;
548 public void setArtifactType(String artifactType
) {
549 this.artifactType
= artifactType
;