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; } }