// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.jdisc.core; import com.yahoo.io.IOUtils; import com.yahoo.yolean.Exceptions; import org.apache.felix.framework.Felix; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.framework.launch.Framework; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.jar.JarInputStream; import java.util.logging.Logger; /** * @author Simon Thoresen Hult * @author gjoranv */ public class ExportPackages { private static final Logger log = Logger.getLogger(ExportPackages.class.getName()); public static final String PROPERTIES_FILE = "/exportPackages.properties"; public static final String EXPORT_PACKAGES = "exportPackages"; private static final String REPLACE_VERSION_PREFIX = "__REPLACE_VERSION__"; public static void main(String[] args) throws IOException { String fileName = args[0]; if (!fileName.endsWith(PROPERTIES_FILE)) { throw new IllegalArgumentException("Expected '" + PROPERTIES_FILE + "', got '" + fileName + "'."); } String exportPackages = getExportPackages(args); Properties props = new Properties(); props.setProperty(EXPORT_PACKAGES, exportPackages); try (FileWriter writer = new FileWriter(new File(fileName))) { props.store(writer, "generated by " + ExportPackages.class.getName()); } } // Make sure to update the junit integration test `ExportPackagesIT.java` if the set of exported packages is modified. private static String getExportPackages(String[] jars) throws IOException { StringBuilder out = new StringBuilder(); out.append(getSystemPackages()).append(", ") .append("com.yahoo.jdisc, ") .append("com.yahoo.jdisc.application, ") .append("com.yahoo.jdisc.handler, ") .append("com.yahoo.jdisc.service, ") .append("com.yahoo.jdisc.statistics, ") .append("com.yahoo.jdisc.refcount, ") .append("javax.inject;version=1.0.0, ") // TODO Vespa 9: remove. Included in guice, but not exported. Needed by container-jersey. .append("org.aopalliance.intercept, ") .append("org.aopalliance.aop"); for (int i = 1; i < jars.length; ++i) { String exports = getExportedPackages(jars[i]); if (exports != null && ! exports.isEmpty()) out.append(", ").append(exports); } return out.toString(); } public static String readExportProperty() { Properties props = new Properties(); try { props.load(ExportPackages.class.getResourceAsStream(PROPERTIES_FILE)); } catch (IOException e) { throw new IllegalStateException("Failed to read resource '" + PROPERTIES_FILE + "'."); } return props.getProperty(EXPORT_PACKAGES); } public static String getSystemPackages() { File cache; try { cache = Files.createTempDirectory("felix-cache").toAbsolutePath().toFile(); } catch (IOException e) { throw new RuntimeException("Could not create temp bundle-cache.", e); } Framework framework = new Felix(felixCacheParams(cache.getAbsolutePath())); try { framework.init(); framework.start(); return framework.getHeaders().get(Constants.EXPORT_PACKAGE); } catch (BundleException e) { throw new RuntimeException("Failed retrieving exported system packages. ", e); } finally { try { framework.stop(); framework.waitForStop(10000); } catch (BundleException | InterruptedException e) { log.warning("Failed to stop Felix framework:\n" + Exceptions.toMessageString(e)); } if (! IOUtils.recursiveDeleteDir(cache)) { log.warning("Failed to delete temp dir, must be deleted manually: " + cache.getAbsolutePath()); } } } private static Map felixCacheParams(String cache) { Map params = new HashMap<>(); params.put("felix.cache.profiledir", cache); params.put("felix.cache.dir", cache); params.put(Constants.FRAMEWORK_STORAGE, cache); return params; } private static String getExportedPackages(String argument) throws IOException { if (argument.startsWith(REPLACE_VERSION_PREFIX)) { String jarFile = argument.substring(REPLACE_VERSION_PREFIX.length()); return readExportHeader(jarFile); } else { return readExportHeader(argument); } } private static String readExportHeader(String jarFile) throws IOException { try (JarInputStream jar = new JarInputStream(new FileInputStream(jarFile))) { return jar.getManifest().getMainAttributes().getValue(Constants.EXPORT_PACKAGE); } } }