ReleaseInfoPlugin.java revision 16245:c510a5c610e1
1/*
2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package jdk.tools.jlink.internal.plugins;
26
27import java.io.ByteArrayOutputStream;
28import java.io.FileInputStream;
29import java.io.IOException;
30import java.io.UncheckedIOException;
31import java.lang.module.ModuleDescriptor;
32import java.util.EnumSet;
33import java.util.HashMap;
34import java.util.Map;
35import java.util.Optional;
36import java.util.Properties;
37import java.util.Set;
38import java.util.function.Function;
39import java.util.stream.Collectors;
40import jdk.tools.jlink.internal.ModuleSorter;
41import jdk.tools.jlink.internal.Utils;
42import jdk.tools.jlink.plugin.ResourcePool;
43import jdk.tools.jlink.plugin.ResourcePoolBuilder;
44import jdk.tools.jlink.plugin.ResourcePoolEntry;
45import jdk.tools.jlink.plugin.ResourcePoolModule;
46import jdk.tools.jlink.plugin.Plugin.Category;
47import jdk.tools.jlink.plugin.Plugin.State;
48import jdk.tools.jlink.plugin.Plugin;
49
50/**
51 * This plugin adds/deletes information for 'release' file.
52 */
53public final class ReleaseInfoPlugin implements Plugin {
54    // option name
55    public static final String NAME = "release-info";
56    public static final String KEYS = "keys";
57    private final Map<String, String> release = new HashMap<>();
58
59    @Override
60    public Category getType() {
61        return Category.METAINFO_ADDER;
62    }
63
64    @Override
65    public String getName() {
66        return NAME;
67    }
68
69    @Override
70    public String getDescription() {
71        return PluginsResourceBundle.getDescription(NAME);
72    }
73
74    @Override
75    public Set<State> getState() {
76        return EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL);
77    }
78
79    @Override
80    public boolean hasArguments() {
81        return true;
82    }
83
84    @Override
85    public String getArgumentsDescription() {
86        return PluginsResourceBundle.getArgument(NAME);
87    }
88
89    @Override
90    public void configure(Map<String, String> config) {
91        String operation = config.get(NAME);
92        if (operation == null) {
93            return;
94        }
95
96        switch (operation) {
97            case "add": {
98                // leave it to open-ended! source, java_version, java_full_version
99                // can be passed via this option like:
100                //
101                //     --release-info add:build_type=fastdebug,source=openjdk,java_version=9
102                // and put whatever value that was passed in command line.
103
104                config.keySet().stream().
105                    filter(s -> !NAME.equals(s)).
106                    forEach(s -> release.put(s, config.get(s)));
107            }
108            break;
109
110            case "del": {
111                // --release-info del:keys=openjdk,java_version
112                Utils.parseList(config.get(KEYS)).stream().forEach((k) -> {
113                    release.remove(k);
114                });
115            }
116            break;
117
118            default: {
119                // --release-info <file>
120                Properties props = new Properties();
121                try (FileInputStream fis = new FileInputStream(operation)) {
122                    props.load(fis);
123                } catch (IOException exp) {
124                    throw new UncheckedIOException(exp);
125                }
126                props.forEach((k, v) -> release.put(k.toString(), v.toString()));
127            }
128            break;
129        }
130    }
131
132    @Override
133    public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
134        in.transformAndCopy(Function.identity(), out);
135
136        Optional<ResourcePoolModule> javaBase = in.moduleView().findModule("java.base");
137        javaBase.ifPresent(mod -> {
138            // fill release information available from transformed "java.base" module!
139            ModuleDescriptor desc = mod.descriptor();
140            desc.osName().ifPresent(s -> {
141                release.put("OS_NAME", quote(s));
142            });
143            desc.osVersion().ifPresent(s -> release.put("OS_VERSION", quote(s)));
144            desc.osArch().ifPresent(s -> release.put("OS_ARCH", quote(s)));
145            desc.version().ifPresent(s -> release.put("JAVA_VERSION",
146                    quote(parseVersion(s.toString()))));
147            desc.version().ifPresent(s -> release.put("JAVA_FULL_VERSION",
148                    quote(s.toString())));
149        });
150
151        // put topological sorted module names separated by space
152        release.put("MODULES",  new ModuleSorter(in.moduleView())
153                .sorted().map(ResourcePoolModule::name)
154                .collect(Collectors.joining(" ", "\"", "\"")));
155
156        // create a TOP level ResourcePoolEntry for "release" file.
157        out.add(ResourcePoolEntry.create("/java.base/release",
158            ResourcePoolEntry.Type.TOP, releaseFileContent()));
159        return out.build();
160    }
161
162    // Parse version string and return a string that includes only version part
163    // leaving "pre", "build" information. See also: java.lang.Runtime.Version.
164    private static String parseVersion(String str) {
165        return Runtime.Version.parse(str).
166            version().
167            stream().
168            map(Object::toString).
169            collect(Collectors.joining("."));
170    }
171
172    private static String quote(String str) {
173        return "\"" + str + "\"";
174    }
175
176    private byte[] releaseFileContent() {
177        Properties props = new Properties();
178        props.putAll(release);
179        ByteArrayOutputStream baos = new ByteArrayOutputStream();
180        try {
181            props.store(baos, "");
182            return baos.toByteArray();
183        } catch (IOException ex) {
184            throw new UncheckedIOException(ex);
185        }
186    }
187}
188