ReleaseInfoPlugin.java revision 17000:fa3c4a60a616
1219820Sjeff/*
2219820Sjeff * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3219820Sjeff * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4219820Sjeff *
5219820Sjeff * This code is free software; you can redistribute it and/or modify it
6219820Sjeff * under the terms of the GNU General Public License version 2 only, as
7219820Sjeff * published by the Free Software Foundation.  Oracle designates this
8219820Sjeff * particular file as subject to the "Classpath" exception as provided
9219820Sjeff * by Oracle in the LICENSE file that accompanied this code.
10219820Sjeff *
11219820Sjeff * This code is distributed in the hope that it will be useful, but WITHOUT
12219820Sjeff * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13219820Sjeff * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14219820Sjeff * version 2 for more details (a copy is included in the LICENSE file that
15219820Sjeff * accompanied this code).
16219820Sjeff *
17219820Sjeff * You should have received a copy of the GNU General Public License version
18219820Sjeff * 2 along with this work; if not, write to the Free Software Foundation,
19219820Sjeff * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20219820Sjeff *
21219820Sjeff * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22219820Sjeff * or visit www.oracle.com if you need additional information or have any
23219820Sjeff * questions.
24219820Sjeff */
25219820Sjeffpackage jdk.tools.jlink.internal.plugins;
26219820Sjeff
27219820Sjeffimport java.io.ByteArrayOutputStream;
28219820Sjeffimport java.io.FileInputStream;
29219820Sjeffimport java.io.IOException;
30219820Sjeffimport java.io.PrintWriter;
31219820Sjeffimport java.io.UncheckedIOException;
32219820Sjeffimport java.lang.module.ModuleDescriptor;
33219820Sjeffimport java.util.EnumSet;
34219820Sjeffimport java.util.HashMap;
35219820Sjeffimport java.util.Map;
36219820Sjeffimport java.util.Optional;
37219820Sjeffimport java.util.Properties;
38219820Sjeffimport java.util.Set;
39219820Sjeffimport java.util.function.Function;
40219820Sjeffimport java.util.stream.Collectors;
41219820Sjeffimport jdk.tools.jlink.internal.ModuleSorter;
42219820Sjeffimport jdk.tools.jlink.internal.Utils;
43255932Salfredimport jdk.tools.jlink.plugin.ResourcePool;
44219820Sjeffimport jdk.tools.jlink.plugin.ResourcePoolBuilder;
45255932Salfredimport jdk.tools.jlink.plugin.ResourcePoolEntry;
46255932Salfredimport jdk.tools.jlink.plugin.ResourcePoolModule;
47255932Salfredimport jdk.tools.jlink.plugin.Plugin;
48255932Salfred
49255932Salfred/**
50255932Salfred * This plugin adds/deletes information for 'release' file.
51255932Salfred */
52255932Salfredpublic final class ReleaseInfoPlugin implements Plugin {
53255932Salfred    // option name
54255932Salfred    public static final String NAME = "release-info";
55255932Salfred    public static final String KEYS = "keys";
56255932Salfred    private final Map<String, String> release = new HashMap<>();
57255932Salfred
58255932Salfred    @Override
59255932Salfred    public Category getType() {
60255932Salfred        return Category.METAINFO_ADDER;
61255932Salfred    }
62255932Salfred
63255932Salfred    @Override
64255932Salfred    public String getName() {
65255932Salfred        return NAME;
66255932Salfred    }
67255932Salfred
68255932Salfred    @Override
69255932Salfred    public String getDescription() {
70255932Salfred        return PluginsResourceBundle.getDescription(NAME);
71255932Salfred    }
72255932Salfred
73255932Salfred    @Override
74255932Salfred    public Set<State> getState() {
75255932Salfred        return EnumSet.of(State.AUTO_ENABLED, State.FUNCTIONAL);
76255932Salfred    }
77255932Salfred
78255932Salfred    @Override
79255932Salfred    public boolean hasArguments() {
80255932Salfred        return true;
81255932Salfred    }
82255932Salfred
83255932Salfred    @Override
84255932Salfred    public String getArgumentsDescription() {
85219820Sjeff        return PluginsResourceBundle.getArgument(NAME);
86255932Salfred    }
87219820Sjeff
88219820Sjeff    @Override
89255932Salfred    public void configure(Map<String, String> config) {
90255932Salfred        String operation = config.get(NAME);
91219820Sjeff        if (operation == null) {
92219820Sjeff            return;
93255932Salfred        }
94219820Sjeff
95219820Sjeff        switch (operation) {
96255932Salfred            case "add": {
97255932Salfred                // leave it to open-ended! source, java_version, java_full_version
98219820Sjeff                // can be passed via this option like:
99255932Salfred                //
100255932Salfred                //     --release-info add:build_type=fastdebug,source=openjdk,java_version=9
101255932Salfred                // and put whatever value that was passed in command line.
102255932Salfred
103255932Salfred                config.keySet().stream()
104255932Salfred                      .filter(s -> !NAME.equals(s))
105255932Salfred                      .forEach(s -> release.put(s, config.get(s)));
106255932Salfred            }
107255932Salfred            break;
108255932Salfred
109255932Salfred            case "del": {
110219820Sjeff                // --release-info del:keys=openjdk,java_version
111219820Sjeff                Utils.parseList(config.get(KEYS)).stream().forEach((k) -> {
112219820Sjeff                    release.remove(k);
113255932Salfred                });
114255932Salfred            }
115255932Salfred            break;
116219820Sjeff
117219820Sjeff            default: {
118219820Sjeff                // --release-info <file>
119219820Sjeff                Properties props = new Properties();
120219820Sjeff                try (FileInputStream fis = new FileInputStream(operation)) {
121219820Sjeff                    props.load(fis);
122219820Sjeff                } catch (IOException exp) {
123255932Salfred                    throw new UncheckedIOException(exp);
124255932Salfred                }
125255932Salfred                props.forEach((k, v) -> release.put(k.toString(), v.toString()));
126255932Salfred            }
127255932Salfred            break;
128255932Salfred        }
129255932Salfred    }
130255932Salfred
131255932Salfred    @Override
132255932Salfred    public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
133255932Salfred        in.transformAndCopy(Function.identity(), out);
134255932Salfred
135255932Salfred        Optional<ResourcePoolModule> javaBase = in.moduleView().findModule("java.base");
136255932Salfred        javaBase.ifPresent(mod -> {
137255932Salfred            // fill release information available from transformed "java.base" module!
138219820Sjeff            ModuleDescriptor desc = mod.descriptor();
139255932Salfred            desc.version().ifPresent(s -> release.put("JAVA_VERSION",
140255932Salfred                    quote(parseVersion(s.toString()))));
141255932Salfred            desc.version().ifPresent(s -> release.put("JAVA_FULL_VERSION",
142255932Salfred                    quote(s.toString())));
143255932Salfred
144255932Salfred            release.put("OS_NAME", quote(mod.osName()));
145255932Salfred            release.put("OS_ARCH", quote(mod.osArch()));
146255932Salfred        });
147255932Salfred
148255932Salfred        // put topological sorted module names separated by space
149255932Salfred        release.put("MODULES",  new ModuleSorter(in.moduleView())
150255932Salfred               .sorted().map(ResourcePoolModule::name)
151255932Salfred               .collect(Collectors.joining(" ", "\"", "\"")));
152255932Salfred
153255932Salfred        // create a TOP level ResourcePoolEntry for "release" file.
154255932Salfred        out.add(ResourcePoolEntry.create("/java.base/release",
155255932Salfred            ResourcePoolEntry.Type.TOP, releaseFileContent()));
156255932Salfred        return out.build();
157255932Salfred    }
158255932Salfred
159255932Salfred    // Parse version string and return a string that includes only version part
160255932Salfred    // leaving "pre", "build" information. See also: java.lang.Runtime.Version.
161255932Salfred    private static String parseVersion(String str) {
162255932Salfred        return Runtime.Version.parse(str)
163255932Salfred                      .version()
164255932Salfred                      .stream()
165255932Salfred                      .map(Object::toString)
166255932Salfred                      .collect(Collectors.joining("."));
167255932Salfred    }
168255932Salfred
169255932Salfred    private static String quote(String str) {
170255932Salfred        return "\"" + str + "\"";
171255932Salfred    }
172255932Salfred
173255932Salfred    private byte[] releaseFileContent() {
174255932Salfred        ByteArrayOutputStream baos = new ByteArrayOutputStream();
175255932Salfred        try (PrintWriter pw = new PrintWriter(baos)) {
176255932Salfred            release.entrySet().stream()
177255932Salfred                   .sorted(Map.Entry.comparingByKey())
178255932Salfred                   .forEach(e -> pw.format("%s=%s%n", e.getKey(), e.getValue()));
179255932Salfred        }
180255932Salfred        return baos.toByteArray();
181255932Salfred    }
182255932Salfred}
183255932Salfred