aboutsummaryrefslogtreecommitdiffstats
path: root/jdisc_core/src/main/java/com/yahoo/jdisc/core/ExportPackages.java
blob: 1ae25aa0cfa0ad70f257bc695a7556ba6cc22af8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// 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<String, Object> felixCacheParams(String cache) {
        Map<String, Object> 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);
        }
    }

}