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
.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
;
18 import javax
.jcr
.Node
;
19 import javax
.jcr
.RepositoryException
;
20 import javax
.jcr
.Session
;
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
;
33 * Creates a jar bundle from an ArgeoOsgiDistribution. This jar is then
34 * persisted and indexed in a java repository using the OSGI Factory.
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>
44 public class ModularDistributionFactory
implements Runnable
{
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";
54 private final static String CSV_FILE_NAME
= "modularDistribution.csv";
55 private final DateFormat snapshotTimestamp
= new SimpleDateFormat("YYYYMMddhhmm");
57 // private final static String FEATURE_FILE_NAME = "feature.xml";
58 // private static int BUFFER_SIZE = 10240;
60 /** Convenience constructor with minimal configuration */
61 public ModularDistributionFactory(OsgiFactory osgiFactory
, ArgeoOsgiDistribution osgiDistribution
) {
62 this.osgiFactory
= osgiFactory
;
63 this.osgiDistribution
= osgiDistribution
;
68 byte[] distFile
= null;
70 javaSession
= osgiFactory
.openJavaSession();
72 if (artifactType
== "jar")
73 distFile
= generateJarFile();
74 else if (artifactType
== "pom")
75 distFile
= generatePomFile();
77 throw new SlcException("Unimplemented distribution artifact type: " + artifactType
+ " for "
78 + osgiDistribution
.toString());
80 // Save in java repository
81 Artifact osgiArtifact
= new DefaultArtifact(osgiDistribution
.getCategory(), osgiDistribution
.getName(),
82 artifactType
, osgiDistribution
.getVersion());
84 Node distNode
= RepoUtils
.copyBytesAsArtifact(javaSession
.getNode(artifactBasePath
), osgiArtifact
,
88 osgiFactory
.indexNode(distNode
);
90 // We use a specific session. Save before closing
92 } catch (RepositoryException e
) {
93 throw new SlcException(
94 "JCR error while persisting modular distribution in JCR " + 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("IO error while generating modular distribution " + osgiDistribution
.toString(), e
);
113 IOUtils
.closeQuietly(byteOut
);
114 IOUtils
.closeQuietly(jarOut
);
118 // private void indexDistribution(Node distNode) throws RepositoryException
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());
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);
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
, "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");
148 // TODO define a user friendly name
149 addManifestAttribute(manifest
, Constants
.BUNDLE_NAME
, osgiDistribution
.getName());
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());
159 addManifestAttribute(manifest
, Constants
.BUNDLE_VERSION
, version
);
164 private void addManifestAttribute(Manifest manifest
, String name
, String value
) {
165 manifest
.getMainAttributes().put(new Attributes
.Name(name
), value
);
168 private byte[] createCsvDescriptor() {
169 Writer writer
= null;
171 // FIXME remove use of tmp file.
172 File tmpFile
= File
.createTempFile("modularDistribution", "csv");
173 tmpFile
.deleteOnExit();
174 writer
= new FileWriter(tmpFile
);
176 for (Iterator
<?
extends NameVersion
> it
= osgiDistribution
.nameVersions(); it
.hasNext();)
177 writer
.write(getCsvLine(it
.next()));
179 return FileUtils
.readFileToByteArray(tmpFile
);
180 } catch (Exception e
) {
181 throw new SlcException("unable to create csv distribution file for " + osgiDistribution
.toString(), e
);
183 IOUtils
.closeQuietly(writer
);
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
193 throw new SlcException("Unimplemented method");
195 // // protected void writeFeatureDescriptor() throws
196 // MojoExecutionException {
197 // File featureDesc = File.createTempFile("feature", "xml");
198 // featureDesc.deleteOnExit();
200 // Writer writer = null;
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());
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";
216 // xmlWriter.addAttribute("version", projectVersion);
218 // Organization organization = project.getOrganization();
219 // if (organization != null && organization.getName() != null)
220 // xmlWriter.addAttribute("provider-name", organization.getName());
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
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
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
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");
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
264 // // deploymentRepository.pathOf(null);
265 // if (jarDirectory == null) {
266 // Set dependencies = mavenDependencyManager
267 // .getTransitiveProjectDependencies(project, remoteRepos,
269 // // // protected void writeFeatureDescriptor() throws
270 // MojoExecutionException {
271 // File featureDesc = File.createTempFile("feature", "xml");
272 // featureDesc.deleteOnExit();
274 // Writer writer = null;
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());
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";
290 // xmlWriter.addAttribute("version", projectVersion);
292 // Organization organization = project.getOrganization();
293 // if (organization != null && organization.getName() != null)
294 // xmlWriter.addAttribute("provider-name", organization.getName());
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
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
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
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");
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
338 // // deploymentRepository.pathOf(null);
339 // if (jarDirectory == null) {
340 // Set dependencies = mavenDependencyManager
341 // .getTransitiveProjectDependencies(project, remoteRepos,
343 // for (Iterator it = dependencies.iterator(); it.hasNext();) {
344 // Artifact artifact = (Artifact) it.next();
345 // writeFeaturePlugin(xmlWriter, artifact.getFile());
348 // // TODO: filter jars
349 // File[] jars = jarDirectory.listFiles();
351 // throw new MojoExecutionException("No jar found in "
353 // for (int i = 0; i < jars.length; i++) {
354 // writeFeaturePlugin(xmlWriter, jars[i]);
358 // xmlWriter.endElement();// feature
360 // if (getLog().isDebugEnabled())
361 // getLog().debug("Wrote Eclipse feature descriptor.");
362 // } catch (Exception e) {
363 // throw new MojoExecutionException("Cannot write feature descriptor",
366 // IOUtil.close(writer);
367 // }for (Iterator it = dependencies.iterator(); it.hasNext();) {
368 // Artifact artifact = (Artifact) it.next();
369 // writeFeaturePlugin(xmlWriter, artifact.getFile());
372 // // TODO: filter jars
373 // File[] jars = jarDirectory.listFiles();
375 // throw new MojoExecutionException("No jar found in "
377 // for (int i = 0; i < jars.length; i++) {
378 // writeFeaturePlugin(xmlWriter, jars[i]);
382 // xmlWriter.endElement();// feature
384 // if (getLog().isDebugEnabled())
385 // getLog().debug("Wrote Eclipse feature descriptor.");
386 // } catch (Exception e) {
387 // throw new MojoExecutionException("Cannot write feature descriptor",
390 // IOUtil.close(writer);
394 /** Create an Aether like distribution artifact */
395 private byte[] generatePomFile() {
396 StringBuilder b
= new StringBuilder();
398 b
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
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>");
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");
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));
421 b
.append("</dependencies>\n");
423 // Dependency management
424 b
.append("<dependencyManagement>\n");
425 b
.append("<dependencies>\n");
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");
432 b
.append("</project>\n");
433 return b
.toString().getBytes();
436 private String
getDependencySnippet(CategoryNameVersion cnv
, boolean includeVersion
) { // , String type, String
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");
443 b
.append("\t<version>").append(cnv
.getVersion()).append("</version>\n");
445 // p.append("\t<type>").append(type).append("</type>\n");
447 // p.append("\t<scope>").append(scope).append("</scope>\n");
448 b
.append("</dependency>\n");
453 private void addToJar(byte[] content
, String name
, JarOutputStream target
) throws IOException
{
454 ByteArrayInputStream in
= null;
456 target
.putNextEntry(new JarEntry(name
));
457 in
= new ByteArrayInputStream(content
);
458 byte[] buffer
= new byte[1024];
460 int count
= in
.read(buffer
);
463 target
.write(buffer
, 0, count
);
467 IOUtils
.closeQuietly(in
);
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();
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());
484 builder
.append(cnv
.getName());
486 builder
.append(cnv
.getVersion());
488 builder
.append(cnv
.getName());
490 builder
.append(cnv
.getVersion());
492 // TODO make this dynamic
493 builder
.append("jar");
494 builder
.append("\n");
496 return builder
.toString();
499 /** Enable dependency injection */
500 public void setOsgiFactory(OsgiFactory osgiFactory
) {
501 this.osgiFactory
= osgiFactory
;
504 public void setOsgiDistribution(ArgeoOsgiDistribution osgiDistribution
) {
505 this.osgiDistribution
= osgiDistribution
;
508 public void setModularDistributionSeparator(String modularDistributionSeparator
) {
509 this.modularDistributionSeparator
= modularDistributionSeparator
;
512 public void setArtifactBasePath(String artifactBasePath
) {
513 this.artifactBasePath
= artifactBasePath
;
516 public void setArtifactType(String artifactType
) {
517 this.artifactType
= artifactType
;