X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.slc.repo%2Fsrc%2Forg%2Fargeo%2Fslc%2Frepo%2FModularDistributionFactory.java;fp=org.argeo.slc.repo%2Fsrc%2Forg%2Fargeo%2Fslc%2Frepo%2FModularDistributionFactory.java;h=19627d8ffdc7db66939a8498d0d7d159716c6092;hb=825d60c5348dbe3f5be25b0bccf7bdebfe694219;hp=0000000000000000000000000000000000000000;hpb=5e991fff5cba01858dcc5747a27e637325bc5c8e;p=gpl%2Fargeo-jcr.git diff --git a/org.argeo.slc.repo/src/org/argeo/slc/repo/ModularDistributionFactory.java b/org.argeo.slc.repo/src/org/argeo/slc/repo/ModularDistributionFactory.java new file mode 100644 index 0000000..19627d8 --- /dev/null +++ b/org.argeo.slc.repo/src/org/argeo/slc/repo/ModularDistributionFactory.java @@ -0,0 +1,519 @@ +package org.argeo.slc.repo; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.argeo.jcr.JcrUtils; +import org.argeo.slc.CategoryNameVersion; +import org.argeo.slc.NameVersion; +import org.argeo.slc.SlcException; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.osgi.framework.Constants; + +/** + * Creates a jar bundle from an ArgeoOsgiDistribution. This jar is then + * persisted and indexed in a java repository using the OSGI Factory. + * + * It does the following + *
  • Creates a Manifest
  • + *
  • Creates files indexes (csv, feature.xml ...)
  • + *
  • Populate the corresponding jar
  • + *
  • Save it in the repository
  • + *
  • Index the node and creates corresponding sha1 and md5 files
  • + * + */ +public class ModularDistributionFactory implements Runnable { + + private OsgiFactory osgiFactory; + private Session javaSession; + private ArgeoOsgiDistribution osgiDistribution; + private String modularDistributionSeparator = ","; + private String artifactBasePath = RepoConstants.DEFAULT_ARTIFACTS_BASE_PATH; + private String artifactType = "jar"; + + // Constants + private final static String CSV_FILE_NAME = "modularDistribution.csv"; + private final DateFormat snapshotTimestamp = new SimpleDateFormat("YYYYMMddhhmm"); + + // private final static String FEATURE_FILE_NAME = "feature.xml"; + // private static int BUFFER_SIZE = 10240; + + /** Convenience constructor with minimal configuration */ + public ModularDistributionFactory(OsgiFactory osgiFactory, ArgeoOsgiDistribution osgiDistribution) { + this.osgiFactory = osgiFactory; + this.osgiDistribution = osgiDistribution; + } + + @Override + public void run() { + byte[] distFile = null; + try { + javaSession = osgiFactory.openJavaSession(); + + if (artifactType == "jar") + distFile = generateJarFile(); + else if (artifactType == "pom") + distFile = generatePomFile(); + else + throw new SlcException("Unimplemented distribution artifact type: " + artifactType + " for " + + osgiDistribution.toString()); + + // Save in java repository + Artifact osgiArtifact = new DefaultArtifact(osgiDistribution.getCategory(), osgiDistribution.getName(), + artifactType, osgiDistribution.getVersion()); + + Node distNode = RepoUtils.copyBytesAsArtifact(javaSession.getNode(artifactBasePath), osgiArtifact, + distFile); + + // index + osgiFactory.indexNode(distNode); + + // We use a specific session. Save before closing + javaSession.save(); + } catch (RepositoryException e) { + throw new SlcException( + "JCR error while persisting modular distribution in JCR " + osgiDistribution.toString(), e); + } finally { + JcrUtils.logoutQuietly(javaSession); + } + } + + private byte[] generateJarFile() { + ByteArrayOutputStream byteOut = null; + JarOutputStream jarOut = null; + try { + byteOut = new ByteArrayOutputStream(); + jarOut = new JarOutputStream(byteOut, createManifest()); + // Create various indexes + addToJar(createCsvDescriptor(), CSV_FILE_NAME, jarOut); + jarOut.close(); + return byteOut.toByteArray(); + } catch (IOException e) { + throw new SlcException("IO error while generating modular distribution " + osgiDistribution.toString(), e); + } finally { + IOUtils.closeQuietly(byteOut); + IOUtils.closeQuietly(jarOut); + } + } + + // private void indexDistribution(Node distNode) throws RepositoryException + // { + // distNode.addMixin(SlcTypes.SLC_MODULAR_DISTRIBUTION); + // distNode.addMixin(SlcTypes.SLC_CATEGORIZED_NAME_VERSION); + // distNode.setProperty(SlcNames.SLC_CATEGORY, + // osgiDistribution.getCategory()); + // distNode.setProperty(SlcNames.SLC_NAME, osgiDistribution.getName()); + // distNode.setProperty(SlcNames.SLC_VERSION, + // osgiDistribution.getVersion()); + // + // if (distNode.hasNode(SlcNames.SLC_MODULES)) + // distNode.getNode(SlcNames.SLC_MODULES).remove(); + // Node modules = distNode.addNode(SlcNames.SLC_MODULES, + // NodeType.NT_UNSTRUCTURED); + // + // for (Iterator it = osgiDistribution + // .nameVersions(); it.hasNext();) + // addModule(modules, it.next()); + // } + + private Manifest createManifest() { + Manifest manifest = new Manifest(); + + // TODO make this configurable + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); +// addManifestAttribute(manifest, Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, "JavaSE-1.8"); + addManifestAttribute(manifest, Constants.BUNDLE_VENDOR, "Argeo"); + addManifestAttribute(manifest, Constants.BUNDLE_MANIFESTVERSION, "2"); +// addManifestAttribute(manifest, "Bundle-License", "http://www.apache.org/licenses/LICENSE-2.0.txt"); + + // TODO define a user friendly name + addManifestAttribute(manifest, Constants.BUNDLE_NAME, osgiDistribution.getName()); + + // Categorized name version + addManifestAttribute(manifest, RepoConstants.SLC_CATEGORY_ID, osgiDistribution.getCategory()); + addManifestAttribute(manifest, Constants.BUNDLE_SYMBOLICNAME, osgiDistribution.getName()); + String version = osgiDistribution.getVersion(); + if (version.endsWith("-SNAPSHOT")) { + version = version.substring(0, version.length() - "-SNAPSHOT".length()); + version = version + ".SNAPSHOT-r" + snapshotTimestamp.format(new Date()); + } + addManifestAttribute(manifest, Constants.BUNDLE_VERSION, version); + + return manifest; + } + + private void addManifestAttribute(Manifest manifest, String name, String value) { + manifest.getMainAttributes().put(new Attributes.Name(name), value); + } + + private byte[] createCsvDescriptor() { + Writer writer = null; + try { + // FIXME remove use of tmp file. + File tmpFile = File.createTempFile("modularDistribution", "csv"); + tmpFile.deleteOnExit(); + writer = new FileWriter(tmpFile); + // Populate the file + for (Iterator it = osgiDistribution.nameVersions(); it.hasNext();) + writer.write(getCsvLine(it.next())); + writer.flush(); + return FileUtils.readFileToByteArray(tmpFile); + } catch (Exception e) { + throw new SlcException("unable to create csv distribution file for " + osgiDistribution.toString(), e); + } finally { + IOUtils.closeQuietly(writer); + } + } + + @SuppressWarnings("unused") + private byte[] createFeatureDescriptor() { + // Directly retrieved from Argeo maven plugin + // Does not work due to the lack of org.codehaus.plexus/plexus-archiver + // third party dependency + + throw new SlcException("Unimplemented method"); + + // // protected void writeFeatureDescriptor() throws + // MojoExecutionException { + // File featureDesc = File.createTempFile("feature", "xml"); + // featureDesc.deleteOnExit(); + // + // Writer writer = null; + // try { + // writer = new FileWriter(featureDesc); + // PrettyPrintXMLWriter xmlWriter = new PrettyPrintXMLWriter(writer); + // xmlWriter.startElement("feature"); + // xmlWriter.addAttribute("id", project.getArtifactId()); + // xmlWriter.addAttribute("label", project.getName()); + // + // // Version + // String projectVersion = project.getVersion(); + // int indexSnapshot = projectVersion.indexOf("-SNAPSHOT"); + // if (indexSnapshot > -1) + // projectVersion = projectVersion.substring(0, indexSnapshot); + // projectVersion = projectVersion + ".qualifier"; + // + // // project. + // xmlWriter.addAttribute("version", projectVersion); + // + // Organization organization = project.getOrganization(); + // if (organization != null && organization.getName() != null) + // xmlWriter.addAttribute("provider-name", organization.getName()); + // + // if (project.getDescription() != null || project.getUrl() != null) { + // xmlWriter.startElement("description"); + // if (project.getUrl() != null) + // xmlWriter.addAttribute("url", project.getUrl()); + // if (project.getDescription() != null) + // xmlWriter.writeText(project.getDescription()); + // xmlWriter.endElement();// description + // } + // + // if (feature != null && feature.getCopyright() != null + // || (organization != null && organization.getUrl() != null)) { + // xmlWriter.startElement("copyright"); + // if (organization != null && organization.getUrl() != null) + // xmlWriter.addAttribute("url", organization.getUrl()); + // if (feature.getCopyright() != null) + // xmlWriter.writeText(feature.getCopyright()); + // xmlWriter.endElement();// copyright + // } + // + // if (feature != null && feature.getUpdateSite() != null) { + // xmlWriter.startElement("url"); + // xmlWriter.startElement("update"); + // xmlWriter.addAttribute("url", feature.getUpdateSite()); + // xmlWriter.endElement();// update + // xmlWriter.endElement();// url + // } + // + // List licenses = project.getLicenses(); + // if (licenses.size() > 0) { + // // take the first one + // License license = (License) licenses.get(0); + // xmlWriter.startElement("license"); + // + // if (license.getUrl() != null) + // xmlWriter.addAttribute("url", license.getUrl()); + // if (license.getComments() != null) + // xmlWriter.writeText(license.getComments()); + // else if (license.getName() != null) + // xmlWriter.writeText(license.getName()); + // xmlWriter.endElement();// license + // } + // + // // deploymentRepository.pathOf(null); + // if (jarDirectory == null) { + // Set dependencies = mavenDependencyManager + // .getTransitiveProjectDependencies(project, remoteRepos, + // local); + // // // protected void writeFeatureDescriptor() throws + // MojoExecutionException { + // File featureDesc = File.createTempFile("feature", "xml"); + // featureDesc.deleteOnExit(); + // + // Writer writer = null; + // try { + // writer = new FileWriter(featureDesc); + // PrettyPrintXMLWriter xmlWriter = new PrettyPrintXMLWriter(writer); + // xmlWriter.startElement("feature"); + // xmlWriter.addAttribute("id", project.getArtifactId()); + // xmlWriter.addAttribute("label", project.getName()); + // + // // Version + // String projectVersion = project.getVersion(); + // int indexSnapshot = projectVersion.indexOf("-SNAPSHOT"); + // if (indexSnapshot > -1) + // projectVersion = projectVersion.substring(0, indexSnapshot); + // projectVersion = projectVersion + ".qualifier"; + // + // // project. + // xmlWriter.addAttribute("version", projectVersion); + // + // Organization organization = project.getOrganization(); + // if (organization != null && organization.getName() != null) + // xmlWriter.addAttribute("provider-name", organization.getName()); + // + // if (project.getDescription() != null || project.getUrl() != null) { + // xmlWriter.startElement("description"); + // if (project.getUrl() != null) + // xmlWriter.addAttribute("url", project.getUrl()); + // if (project.getDescription() != null) + // xmlWriter.writeText(project.getDescription()); + // xmlWriter.endElement();// description + // } + // + // if (feature != null && feature.getCopyright() != null + // || (organization != null && organization.getUrl() != null)) { + // xmlWriter.startElement("copyright"); + // if (organization != null && organization.getUrl() != null) + // xmlWriter.addAttribute("url", organization.getUrl()); + // if (feature.getCopyright() != null) + // xmlWriter.writeText(feature.getCopyright()); + // xmlWriter.endElement();// copyright + // } + // + // if (feature != null && feature.getUpdateSite() != null) { + // xmlWriter.startElement("url"); + // xmlWriter.startElement("update"); + // xmlWriter.addAttribute("url", feature.getUpdateSite()); + // xmlWriter.endElement();// update + // xmlWriter.endElement();// url + // } + // + // List licenses = project.getLicenses(); + // if (licenses.size() > 0) { + // // take the first one + // License license = (License) licenses.get(0); + // xmlWriter.startElement("license"); + // + // if (license.getUrl() != null) + // xmlWriter.addAttribute("url", license.getUrl()); + // if (license.getComments() != null) + // xmlWriter.writeText(license.getComments()); + // else if (license.getName() != null) + // xmlWriter.writeText(license.getName()); + // xmlWriter.endElement();// license + // } + // + // // deploymentRepository.pathOf(null); + // if (jarDirectory == null) { + // Set dependencies = mavenDependencyManager + // .getTransitiveProjectDependencies(project, remoteRepos, + // local); + // for (Iterator it = dependencies.iterator(); it.hasNext();) { + // Artifact artifact = (Artifact) it.next(); + // writeFeaturePlugin(xmlWriter, artifact.getFile()); + // } + // } else { + // // TODO: filter jars + // File[] jars = jarDirectory.listFiles(); + // if (jars == null) + // throw new MojoExecutionException("No jar found in " + // + jarDirectory); + // for (int i = 0; i < jars.length; i++) { + // writeFeaturePlugin(xmlWriter, jars[i]); + // } + // } + // + // xmlWriter.endElement();// feature + // + // if (getLog().isDebugEnabled()) + // getLog().debug("Wrote Eclipse feature descriptor."); + // } catch (Exception e) { + // throw new MojoExecutionException("Cannot write feature descriptor", + // e); + // } finally { + // IOUtil.close(writer); + // }for (Iterator it = dependencies.iterator(); it.hasNext();) { + // Artifact artifact = (Artifact) it.next(); + // writeFeaturePlugin(xmlWriter, artifact.getFile()); + // } + // } else { + // // TODO: filter jars + // File[] jars = jarDirectory.listFiles(); + // if (jars == null) + // throw new MojoExecutionException("No jar found in " + // + jarDirectory); + // for (int i = 0; i < jars.length; i++) { + // writeFeaturePlugin(xmlWriter, jars[i]); + // } + // } + // + // xmlWriter.endElement();// feature + // + // if (getLog().isDebugEnabled()) + // getLog().debug("Wrote Eclipse feature descriptor."); + // } catch (Exception e) { + // throw new MojoExecutionException("Cannot write feature descriptor", + // e); + // } finally { + // IOUtil.close(writer); + // } + } + + /** Create an Aether like distribution artifact */ + private byte[] generatePomFile() { + StringBuilder b = new StringBuilder(); + // XML header + b.append("\n"); + b.append( + "\n"); + b.append("4.0.0"); + + // Artifact + b.append("").append(osgiDistribution.getCategory()).append("\n"); + b.append("").append(osgiDistribution.getName()).append("\n"); + b.append("").append(osgiDistribution.getVersion()).append("\n"); + b.append("pom\n"); + // p.append("").append("Bundle Name").append("\n"); + // p.append("").append("Bundle + // Description").append("\n"); + + // Dependencies + b.append("\n"); + for (Iterator it = osgiDistribution.nameVersions(); it.hasNext();) { + NameVersion nameVersion = it.next(); + if (!(nameVersion instanceof CategoryNameVersion)) + throw new SlcException("Unsupported type " + nameVersion.getClass()); + CategoryNameVersion nv = (CategoryNameVersion) nameVersion; + b.append(getDependencySnippet(nv, false)); + } + b.append("\n"); + + // Dependency management + b.append("\n"); + b.append("\n"); + + for (Iterator it = osgiDistribution.nameVersions(); it.hasNext();) + b.append(getDependencySnippet((CategoryNameVersion) it.next(), true)); + b.append("\n"); + b.append("\n"); + + b.append("\n"); + return b.toString().getBytes(); + } + + private String getDependencySnippet(CategoryNameVersion cnv, boolean includeVersion) { // , String type, String + // scope + StringBuilder b = new StringBuilder(); + b.append("\n"); + b.append("\t").append(cnv.getCategory()).append("\n"); + b.append("\t").append(cnv.getName()).append("\n"); + if (includeVersion) + b.append("\t").append(cnv.getVersion()).append("\n"); + // if (type!= null) + // p.append("\t").append(type).append("\n"); + // if (type!= null) + // p.append("\t").append(scope).append("\n"); + b.append("\n"); + return b.toString(); + } + + // Helpers + private void addToJar(byte[] content, String name, JarOutputStream target) throws IOException { + ByteArrayInputStream in = null; + try { + target.putNextEntry(new JarEntry(name)); + in = new ByteArrayInputStream(content); + byte[] buffer = new byte[1024]; + while (true) { + int count = in.read(buffer); + if (count == -1) + break; + target.write(buffer, 0, count); + } + target.closeEntry(); + } finally { + IOUtils.closeQuietly(in); + } + } + + private String getCsvLine(NameVersion nameVersion) throws RepositoryException { + if (!(nameVersion instanceof CategoryNameVersion)) + throw new SlcException("Unsupported type " + nameVersion.getClass()); + CategoryNameVersion cnv = (CategoryNameVersion) nameVersion; + StringBuilder builder = new StringBuilder(); + + builder.append(cnv.getName()); + builder.append(modularDistributionSeparator); + builder.append(nameVersion.getVersion()); + builder.append(modularDistributionSeparator); + builder.append(cnv.getCategory().replace('.', '/')); + // MavenConventionsUtils.groupPath("", cnv.getCategory()); + builder.append('/'); + builder.append(cnv.getName()); + builder.append('/'); + builder.append(cnv.getVersion()); + builder.append('/'); + builder.append(cnv.getName()); + builder.append('-'); + builder.append(cnv.getVersion()); + builder.append('.'); + // TODO make this dynamic + builder.append("jar"); + builder.append("\n"); + + return builder.toString(); + } + + /** Enable dependency injection */ + public void setOsgiFactory(OsgiFactory osgiFactory) { + this.osgiFactory = osgiFactory; + } + + public void setOsgiDistribution(ArgeoOsgiDistribution osgiDistribution) { + this.osgiDistribution = osgiDistribution; + } + + public void setModularDistributionSeparator(String modularDistributionSeparator) { + this.modularDistributionSeparator = modularDistributionSeparator; + } + + public void setArtifactBasePath(String artifactBasePath) { + this.artifactBasePath = artifactBasePath; + } + + public void setArtifactType(String artifactType) { + this.artifactType = artifactType; + } +} \ No newline at end of file