X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.slc.repo%2Fsrc%2Forg%2Fargeo%2Fslc%2Frepo%2Fosgi%2FArchiveWrapper.java;fp=org.argeo.slc.repo%2Fsrc%2Forg%2Fargeo%2Fslc%2Frepo%2Fosgi%2FArchiveWrapper.java;h=3cb1e9c8f3264034407568876ea79f5356a7cd33;hb=825d60c5348dbe3f5be25b0bccf7bdebfe694219;hp=0000000000000000000000000000000000000000;hpb=5e991fff5cba01858dcc5747a27e637325bc5c8e;p=gpl%2Fargeo-jcr.git diff --git a/org.argeo.slc.repo/src/org/argeo/slc/repo/osgi/ArchiveWrapper.java b/org.argeo.slc.repo/src/org/argeo/slc/repo/osgi/ArchiveWrapper.java new file mode 100644 index 0000000..3cb1e9c --- /dev/null +++ b/org.argeo.slc.repo/src/org/argeo/slc/repo/osgi/ArchiveWrapper.java @@ -0,0 +1,432 @@ +package org.argeo.slc.repo.osgi; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.jar.JarInputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; +import org.argeo.api.cms.CmsLog; +import org.argeo.jcr.JcrUtils; +import org.argeo.slc.CategoryNameVersion; +import org.argeo.slc.DefaultNameVersion; +import org.argeo.slc.ModuleSet; +import org.argeo.slc.NameVersion; +import org.argeo.slc.SlcException; +import org.argeo.slc.build.Distribution; +import org.argeo.slc.build.License; +import org.argeo.slc.repo.OsgiFactory; +import org.argeo.slc.repo.RepoUtils; +import org.argeo.slc.repo.internal.springutil.AntPathMatcher; +import org.argeo.slc.repo.internal.springutil.PathMatcher; +import org.argeo.slc.repo.maven.ArtifactIdComparator; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.DefaultArtifact; + +import aQute.bnd.osgi.Jar; + +/** + * Download a software distribution and generates the related OSGi bundles from + * the jars, or import them directly if they are already OSGi bundles and don't + * need further modification. + */ +public class ArchiveWrapper implements Runnable, ModuleSet, Distribution { + private final static CmsLog log = CmsLog.getLog(ArchiveWrapper.class); + + private OsgiFactory osgiFactory; + private String version; + private License license; + + private String uri; + + /** Jars to wrap as OSGi bundles */ + private Map wrappers = new HashMap(); + + private SourcesProvider sourcesProvider; + + // pattern of OSGi bundles to import + private PathMatcher pathMatcher = new AntPathMatcher(); + private Map includes = new HashMap(); + private List excludes = new ArrayList(); + + private Boolean mavenGroupIndexes = false; + + public void init() { + for (BndWrapper wrapper : wrappers.values()) { + wrapper.setFactory(this); + if (version != null && wrapper.getVersion() == null) + wrapper.setVersion(version); + if (license != null && wrapper.getLicense() == null) + wrapper.setLicense(license); + } + } + + public void destroy() { + + } + + public String getDistributionId() { + return uri; + } + + public String getVersion() { + return version; + } + + public License getLicense() { + return license; + } + + public String getUri() { + return uri; + } + + public Iterator nameVersions() { + if (wrappers.size() > 0) + return wrappers.values().iterator(); + else + return osgiNameVersions(); + } + + @SuppressWarnings("resource") + protected Iterator osgiNameVersions() { + List nvs = new ArrayList(); + + Session distSession = null; + ZipInputStream zin = null; + try { + distSession = osgiFactory.openDistSession(); + + Node distNode = osgiFactory.getDist(distSession, uri); + zin = new ZipInputStream( + distNode.getNode(Node.JCR_CONTENT).getProperty(Property.JCR_DATA).getBinary().getStream()); + + ZipEntry zentry = null; + entries: while ((zentry = zin.getNextEntry()) != null) { + String name = zentry.getName(); + if (log.isTraceEnabled()) + log.trace("Zip entry " + name); + for (String exclude : excludes) + if (pathMatcher.match(exclude, name)) + continue entries; + + for (String include : includes.keySet()) { + if (pathMatcher.match(include, name)) { + String groupId = includes.get(include); + JarInputStream jis = new JarInputStream(zin); + if (jis.getManifest() == null) { + log.warn("No MANIFEST in entry " + name + ", skipping..."); + continue entries; + } + NameVersion nv = RepoUtils.readNameVersion(jis.getManifest()); + if (nv != null) { + if (nv.getName().endsWith(".source")) + continue entries; + CategoryNameVersion cnv = new ArchiveWrapperCNV(groupId, nv.getName(), nv.getVersion(), + this); + nvs.add(cnv); + // no need to process further includes + continue entries; + } + } + } + } + return nvs.iterator(); + } catch (Exception e) { + throw new SlcException("Cannot wrap distribution " + uri, e); + } finally { + IOUtils.closeQuietly(zin); + JcrUtils.logoutQuietly(distSession); + } + } + + public void run() { + if (mavenGroupIndexes && (version == null)) + throw new SlcException("'mavenGroupIndexes' requires 'version' to be set"); + + Map> binaries = new HashMap>(); + Map> sources = new HashMap>(); + + Session distSession = null; + Session javaSession = null; + ZipInputStream zin = null; + try { + javaSession = osgiFactory.openJavaSession(); + distSession = osgiFactory.openDistSession(); + + if (log.isDebugEnabled()) + log.debug("Wrapping " + uri); + boolean nothingWasDone = true; + + Node distNode = osgiFactory.getDist(distSession, uri); + zin = new ZipInputStream( + distNode.getNode(Node.JCR_CONTENT).getProperty(Property.JCR_DATA).getBinary().getStream()); + + ZipEntry zentry = null; + entries: while ((zentry = zin.getNextEntry()) != null) { + String name = zentry.getName(); + + // sources autodetect + String baseName = FilenameUtils.getBaseName(name); + if (baseName.endsWith("-sources")) { + String bundle = baseName.substring(0, baseName.length() - "-sources".length()); + // log.debug(name + "," + baseName + ", " + bundle); + String bundlePath = FilenameUtils.getPath(name) + bundle + ".jar"; + if (wrappers.containsKey(bundlePath)) { + BndWrapper wrapper = wrappers.get(bundlePath); + NameVersion bundleNv = new DefaultNameVersion(wrapper.getName(), wrapper.getVersion()); + byte[] pdeSource = RepoUtils.packageAsPdeSource(zin, bundleNv); + Artifact sourcesArtifact = new DefaultArtifact(wrapper.getCategory(), + wrapper.getName() + ".source", "jar", wrapper.getVersion()); + Node pdeSourceNode = RepoUtils.copyBytesAsArtifact(javaSession.getRootNode(), sourcesArtifact, + pdeSource); + osgiFactory.indexNode(pdeSourceNode); + pdeSourceNode.getSession().save(); + if (log.isDebugEnabled()) + log.debug("Added sources " + sourcesArtifact + " for bundle " + wrapper.getArtifact() + + "from " + name + " in binary archive."); + } + + } + // else if (baseName.endsWith(".source")) { + // } + + // binaries + if (wrappers.containsKey(name)) { + BndWrapper wrapper = (BndWrapper) wrappers.get(name); + // we must copy since the stream is closed by BND + byte[] origJarBytes = IOUtils.toByteArray(zin); + Artifact artifact = wrapZipEntry(javaSession, zentry, origJarBytes, wrapper); + nothingWasDone = false; + addArtifactToIndex(binaries, wrapper.getGroupId(), artifact); + } else { + for (String wrapperKey : wrappers.keySet()) + if (pathMatcher.match(wrapperKey, name)) { + // first matched is taken + BndWrapper wrapper = (BndWrapper) wrappers.get(wrapperKey); + // we must copy since the stream is closed by BND + byte[] origJarBytes = IOUtils.toByteArray(zin); + Artifact artifact = wrapZipEntry(javaSession, zentry, origJarBytes, wrapper); + nothingWasDone = false; + addArtifactToIndex(binaries, wrapper.getGroupId(), artifact); + continue entries; + } else { + if (log.isTraceEnabled()) + log.trace(name + " not matched by " + wrapperKey); + } + + for (String exclude : excludes) + if (pathMatcher.match(exclude, name)) + continue entries; + + for (String include : includes.keySet()) { + if (pathMatcher.match(include, name)) { + String groupId = includes.get(include); + byte[] origJarBytes = IOUtils.toByteArray(zin); + Artifact artifact = importZipEntry(javaSession, zentry, origJarBytes, groupId); + if (artifact == null) { + log.warn("Skipped non identified " + zentry); + continue entries; + } + nothingWasDone = false; + if (artifact.getArtifactId().endsWith(".source")) + addArtifactToIndex(sources, groupId, artifact); + else + addArtifactToIndex(binaries, groupId, artifact); + // no need to process this entry further + continue entries; + } + } + } + } + + // indexes + if (mavenGroupIndexes && version != null) { + for (String groupId : binaries.keySet()) { + RepoUtils.writeGroupIndexes(javaSession, "/", groupId, version, binaries.get(groupId), + sources.containsKey(groupId) ? sources.get(groupId) : null); + } + } + + if (nothingWasDone) { + log.error("Nothing was done when wrapping " + uri + ". THE DISTRIBUTION IS INCONSISTENT."); + // throw new SlcException("Nothing was done"); + // TODO Fail if not all wrappers matched + } + + } catch (Exception e) { + throw new SlcException("Cannot wrap distribution " + uri, e); + } finally { + IOUtils.closeQuietly(zin); + JcrUtils.logoutQuietly(distSession); + JcrUtils.logoutQuietly(javaSession); + } + } + + protected Artifact wrapZipEntry(Session javaSession, ZipEntry zentry, byte[] origJarBytes, BndWrapper wrapper) + throws RepositoryException { + ByteArrayOutputStream out = null; + ByteArrayInputStream in = null; + Node newJarNode; + Jar jar = null; + try { + out = new ByteArrayOutputStream((int) zentry.getSize()); + in = new ByteArrayInputStream(origJarBytes); + wrapper.wrapJar(in, out); + + Artifact artifact = wrapper.getArtifact(); + newJarNode = RepoUtils.copyBytesAsArtifact(javaSession.getRootNode(), artifact, out.toByteArray()); + osgiFactory.indexNode(newJarNode); + newJarNode.getSession().save(); + if (log.isDebugEnabled()) + log.debug("Wrapped jar " + zentry.getName() + " to " + newJarNode.getPath()); + + if (sourcesProvider != null) + addSource(javaSession, artifact, out.toByteArray()); + + return artifact; + } finally { + IOUtils.closeQuietly(in); + IOUtils.closeQuietly(out); + if (jar != null) + jar.close(); + } + } + + protected void addSource(Session javaSession, Artifact artifact, byte[] binaryJarBytes) { + InputStream in = null; + ByteArrayOutputStream out = null; + Jar jar = null; + try { + in = new ByteArrayInputStream(binaryJarBytes); + jar = new Jar(null, in); + List packages = jar.getPackages(); + + out = new ByteArrayOutputStream(); + sourcesProvider.writeSources(packages, new ZipOutputStream(out)); + + IOUtils.closeQuietly(in); + in = new ByteArrayInputStream(out.toByteArray()); + byte[] sourcesJar = RepoUtils.packageAsPdeSource(in, + new DefaultNameVersion(artifact.getArtifactId(), artifact.getVersion())); + Artifact sourcesArtifact = new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId() + ".source", + "jar", artifact.getVersion()); + Node sourcesJarNode = RepoUtils.copyBytesAsArtifact(javaSession.getRootNode(), sourcesArtifact, sourcesJar); + sourcesJarNode.getSession().save(); + + if (log.isDebugEnabled()) + log.debug("Added sources " + sourcesArtifact + " for bundle " + artifact + "from source provider " + + sourcesProvider); + } catch (Exception e) { + throw new SlcException("Cannot get sources for " + artifact, e); + } finally { + IOUtils.closeQuietly(in); + IOUtils.closeQuietly(out); + if (jar != null) + jar.close(); + } + } + + protected Artifact importZipEntry(Session javaSession, ZipEntry zentry, byte[] binaryJarBytes, String groupId) + throws RepositoryException { + ByteArrayInputStream in = null; + Node newJarNode; + try { + in = new ByteArrayInputStream(binaryJarBytes); + NameVersion nameVersion = RepoUtils.readNameVersion(in); + if (nameVersion == null) { + log.warn("Cannot identify " + zentry.getName()); + return null; + } + Artifact artifact = new DefaultArtifact(groupId, nameVersion.getName(), "jar", nameVersion.getVersion()); + newJarNode = RepoUtils.copyBytesAsArtifact(javaSession.getRootNode(), artifact, binaryJarBytes); + osgiFactory.indexNode(newJarNode); + newJarNode.getSession().save(); + if (log.isDebugEnabled()) { + log.debug(zentry.getName() + " => " + artifact); + } + + if (sourcesProvider != null) + addSource(javaSession, artifact, binaryJarBytes); + + return artifact; + } finally { + IOUtils.closeQuietly(in); + } + } + + private void addArtifactToIndex(Map> index, String groupId, Artifact artifact) { + if (!index.containsKey(groupId)) + index.put(groupId, new TreeSet(new ArtifactIdComparator())); + index.get(groupId).add(artifact); + } + + public void setUri(String uri) { + this.uri = uri; + } + + public void setWrappers(Map wrappers) { + this.wrappers = wrappers; + } + + public void setOsgiFactory(OsgiFactory osgiFactory) { + this.osgiFactory = osgiFactory; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setLicense(License license) { + this.license = license; + } + + public void setPathMatcher(PathMatcher pathMatcher) { + this.pathMatcher = pathMatcher; + } + + public void setIncludes(Map includes) { + this.includes = includes; + } + + public void setExcludes(List excludes) { + this.excludes = excludes; + } + + public void setMavenGroupIndexes(Boolean mavenGroupIndexes) { + this.mavenGroupIndexes = mavenGroupIndexes; + } + + public void setSourcesProvider(SourcesProvider sourcesProvider) { + this.sourcesProvider = sourcesProvider; + } + + public Map getWrappers() { + return wrappers; + } + + public Map getIncludes() { + return includes; + } + + public List getExcludes() { + return excludes; + } + +}