package aQute.bnd.main; import static aQute.bnd.osgi.Constants.REMOVEHEADERS; import java.io.ByteArrayOutputStream; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.jar.Attributes; import java.util.jar.Manifest; import aQute.bnd.annotation.plugin.BndPlugin; import aQute.bnd.build.Container; import aQute.bnd.build.Project; import aQute.bnd.header.Attrs; import aQute.bnd.header.Parameters; import aQute.bnd.osgi.Constants; import aQute.bnd.osgi.Domain; import aQute.bnd.osgi.EmbeddedResource; import aQute.bnd.osgi.FileResource; import aQute.bnd.osgi.Instructions; import aQute.bnd.osgi.Jar; import aQute.bnd.osgi.JarResource; import aQute.bnd.osgi.Processor; import aQute.bnd.osgi.Resource; import aQute.bnd.osgi.Verifier; import aQute.bnd.service.Strategy; import aQute.bnd.service.export.Exporter; import aQute.lib.collections.MultiMap; @BndPlugin(name = "subsystem") public class SubsystemExporter implements Exporter { private static final String OSGI_INF_SUBSYSTEM_MF = "OSGI-INF/SUBSYSTEM.MF"; private static final String SUBSYSTEM_SYMBOLIC_NAME = "Subsystem-SymbolicName"; private static final String OSGI_SUBSYSTEM_APPLICATION = "osgi.subsystem.application"; private static final String OSGI_SUBSYSTEM_FEATURE = "osgi.subsystem.feature"; private static final String OSGI_SUBSYSTEM_COMPOSITE = "osgi.subsystem.composite"; private static final String SUBSYSTEM_TYPE = "Subsystem-Type"; @SuppressWarnings("unused") private static final String SUBSYSTEM_CONTENT = "Subsystem-Content"; @Override public String[] getTypes() { return new String[] { OSGI_SUBSYSTEM_APPLICATION, OSGI_SUBSYSTEM_FEATURE, OSGI_SUBSYSTEM_COMPOSITE }; } @Override public Map.Entry<String,Resource> export(String type, final Project project, Map<String,String> options) throws Exception { Jar jar = new Jar("."); project.addClose(jar); Manifest manifest = new Manifest(); manifest.getMainAttributes().putValue("Manifest-Version", "1.0"); manifest.getMainAttributes().putValue("Subsystem-ManifestVersion", "1"); List<Container> distro = project.getBundles(Strategy.LOWEST, project.getProperty(Constants.DISTRO), Constants.DISTRO); List<File> distroFiles = getBundles(distro, project); List<File> files = getBundles(project.getRunbundles(), project); MultiMap<String,Attrs> imports = new MultiMap<String,Attrs>(); MultiMap<String,Attrs> exports = new MultiMap<String,Attrs>(); Parameters requirements = new Parameters(); Parameters capabilities = new Parameters(); for (File file : files) { Domain domain = Domain.domain(file); String bsn = domain.getBundleSymbolicName().getKey(); String version = domain.getBundleVersion(); for (Entry<String,Attrs> e : domain.getImportPackage().entrySet()) { imports.add(e.getKey(), e.getValue()); } for (Entry<String,Attrs> e : domain.getExportPackage().entrySet()) { exports.add(e.getKey(), e.getValue()); } String path = bsn + "-" + version + ".jar"; jar.putResource(path, new FileResource(file)); } headers(project, manifest.getMainAttributes()); set(manifest.getMainAttributes(), SUBSYSTEM_TYPE, OSGI_SUBSYSTEM_FEATURE); String ssn = project.getName(); Collection<String> bsns = project.getBsns(); if (bsns.size() > 0) { ssn = bsns.iterator().next(); } set(manifest.getMainAttributes(), SUBSYSTEM_SYMBOLIC_NAME, ssn); ByteArrayOutputStream bout = new ByteArrayOutputStream(); manifest.write(bout); jar.putResource(OSGI_INF_SUBSYSTEM_MF, new EmbeddedResource(bout.toByteArray(), 0)); final JarResource jarResource = new JarResource(jar); final String name = ssn + ".esa"; return new Map.Entry<String,Resource>() { @Override public String getKey() { return name; } @Override public Resource getValue() { return jarResource; } @Override public Resource setValue(Resource arg0) { throw new UnsupportedOperationException(); } }; } private List<File> getBundles(Collection<Container> bundles, Processor reporter) throws Exception { List<File> files = new ArrayList<File>(); for (Container c : bundles) { switch (c.getType()) { case ERROR : // skip, already reported break; case PROJECT : case EXTERNAL : case REPO : files.add(c.getFile()); break; case LIBRARY : c.contributeFiles(files, reporter); break; } } return files; } private void headers(final Project project, Attributes application) { for (String key : project.getPropertyKeys(true)) { if (!Verifier.HEADER_PATTERN.matcher(key).matches()) continue; if (application.getValue(key) != null) continue; String value = project.getProperty(key); if (value == null) continue; value = value.trim(); if (value.isEmpty() || Constants.EMPTY_HEADER.equals(value)) continue; char c = value.charAt(0); if (Character.isUpperCase(c)) application.putValue(key, value); } Instructions instructions = new Instructions(project.mergeProperties(REMOVEHEADERS)); Collection<Object> result = instructions.select(application.keySet(), false); application.keySet().removeAll(result); } private void set(Attributes application, String key, String... values) { if (application.getValue(key) != null) return; for (String value : values) { if (value != null) { application.putValue(key, value); return; } } } }