src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java
branchJDK-8200758-branch
changeset 58696 61c44899b4eb
parent 58414 a5f66aa04f68
child 58762 0fe62353385b
equal deleted inserted replaced
58695:64adf683bc7b 58696:61c44899b4eb
    22  * or visit www.oracle.com if you need additional information or have any
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 package jdk.jpackage.internal;
    25 package jdk.jpackage.internal;
    26 
    26 
    27 import java.io.BufferedWriter;
       
    28 import java.io.FileInputStream;
    27 import java.io.FileInputStream;
    29 import java.io.FileWriter;
       
    30 import java.io.IOException;
    28 import java.io.IOException;
    31 import java.io.Writer;
       
    32 import java.nio.file.Path;
    29 import java.nio.file.Path;
    33 import java.util.ArrayList;
       
    34 import java.util.Collections;
       
    35 import java.util.List;
    30 import java.util.List;
    36 import java.util.ArrayList;
    31 import java.util.ArrayList;
    37 import java.util.Map;
    32 import java.util.Map;
    38 import java.util.regex.Matcher;
       
    39 import java.util.regex.Pattern;
       
    40 import java.util.stream.Collectors;
       
    41 import javax.xml.parsers.DocumentBuilder;
    33 import javax.xml.parsers.DocumentBuilder;
    42 import javax.xml.parsers.DocumentBuilderFactory;
    34 import javax.xml.parsers.DocumentBuilderFactory;
    43 import javax.xml.parsers.ParserConfigurationException;
    35 import javax.xml.parsers.ParserConfigurationException;
    44 import javax.xml.stream.XMLOutputFactory;
       
    45 import javax.xml.stream.XMLStreamException;
       
    46 import javax.xml.stream.XMLStreamWriter;
       
    47 import javax.xml.xpath.XPath;
    36 import javax.xml.xpath.XPath;
    48 import javax.xml.xpath.XPathConstants;
    37 import javax.xml.xpath.XPathConstants;
    49 import javax.xml.xpath.XPathExpressionException;
    38 import javax.xml.xpath.XPathExpressionException;
    50 import javax.xml.xpath.XPathFactory;
    39 import javax.xml.xpath.XPathFactory;
    51 import org.w3c.dom.Document;
    40 import org.w3c.dom.Document;
    52 import org.w3c.dom.NodeList;
    41 import org.w3c.dom.NodeList;
    53 import org.xml.sax.SAXException;
    42 import org.xml.sax.SAXException;
    54 
    43 
    55 import static jdk.jpackage.internal.StandardBundlerParam.*;
    44 import static jdk.jpackage.internal.StandardBundlerParam.*;
    56 
    45 
    57 class AppImageFile {
    46 public class AppImageFile {
    58 
    47 
    59     // These values will be loaded from AppImage xml file.
    48     // These values will be loaded from AppImage xml file.
    60     private final String creatorVersion;
    49     private final String creatorVersion;
    61     private final String creatorPlatform;
    50     private final String creatorPlatform;
    62     private final String launcherName;
    51     private final String launcherName;
    63     private final List<String> addLauncherNames;
    52     private final List<String> addLauncherNames;
    64     
    53 
    65     final static String XML_FILENAME = ".jpackage.xml";
    54     public final static String FILENAME = ".jpackage.xml";
    66     
    55 
    67     private final static Map<Platform, String> PLATFORM_LABELS = Map.of(
    56     private final static Map<Platform, String> PLATFORM_LABELS = Map.of(
    68             Platform.LINUX, "linux", Platform.WINDOWS, "windows", Platform.MAC,
    57             Platform.LINUX, "linux", Platform.WINDOWS, "windows", Platform.MAC,
    69             "macOS");
    58             "macOS");
    70     
    59 
    71 
    60 
    72     private AppImageFile() {
    61     private AppImageFile() {
    73         this(null, null, null, null);
    62         this(null, null, null, null);
    74     }
    63     }
    75     
    64 
    76     private AppImageFile(String launcherName, List<String> addLauncherNames,
    65     private AppImageFile(String launcherName, List<String> addLauncherNames,
    77             String creatorVersion, String creatorPlatform) {
    66             String creatorVersion, String creatorPlatform) {
    78         this.launcherName = launcherName;
    67         this.launcherName = launcherName;
    79         this.addLauncherNames = addLauncherNames;
    68         this.addLauncherNames = addLauncherNames;
    80         this.creatorVersion = creatorVersion;
    69         this.creatorVersion = creatorVersion;
    81         this.creatorPlatform = creatorPlatform;
    70         this.creatorPlatform = creatorPlatform;
    82     }
    71     }
    83 
    72 
    84     /**
    73     /**
    85      * Would return null to indicate stored command line is invalid.
    74      * Returns list of additional launchers configured for the application.
       
    75      * Each item in the list is not null or empty string.
       
    76      * Returns empty list for application without additional launchers.
    86      */
    77      */
    87     List<String> getAddLauncherNames() {
    78     List<String> getAddLauncherNames() {
    88         return addLauncherNames;
    79         return addLauncherNames;
    89     }
    80     }
    90 
    81 
       
    82     /**
       
    83      * Returns main application launcher name. Never returns null or empty value.
       
    84      */
    91     String getLauncherName() {
    85     String getLauncherName() {
    92         return launcherName;
    86         return launcherName;
    93     }
    87     }
    94     
    88 
    95     void verifyCompatible() throws ConfigException {
    89     void verifyCompatible() throws ConfigException {
    96         // Just do nohing for now.
    90         // Just do nothing for now.
    97     }
    91     }
    98 
    92 
       
    93     /**
       
    94      * Saves file with application image info in application image.
       
    95      * @param appImageDir - path to application image
       
    96      * @throws IOException
       
    97      */
    99     static void save(Path appImage, Map<String, Object> params)
    98     static void save(Path appImage, Map<String, Object> params)
   100             throws IOException {
    99             throws IOException {
   101         Path xmlFile = appImage.resolve(XML_FILENAME);
   100         IOUtils.createXml(appImage.resolve(FILENAME), xml -> {
   102         XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance();
       
   103 
       
   104         try (Writer w = new BufferedWriter(new FileWriter(xmlFile.toFile()))) {
       
   105             XMLStreamWriter xml = xmlFactory.createXMLStreamWriter(w);
       
   106 
       
   107             xml.writeStartDocument();
       
   108             xml.writeStartElement("jpackage-state");
   101             xml.writeStartElement("jpackage-state");
   109             xml.writeAttribute("version", getVersion());
   102             xml.writeAttribute("version", getVersion());
   110             xml.writeAttribute("platform", getPlatform());
   103             xml.writeAttribute("platform", getPlatform());
   111             
   104 
   112             xml.writeStartElement("main-launcher");
   105             xml.writeStartElement("main-launcher");
   113             xml.writeCharacters(APP_NAME.fetchFrom(params));
   106             xml.writeCharacters(APP_NAME.fetchFrom(params));
   114             xml.writeEndElement();
   107             xml.writeEndElement();
   115             
   108 
   116             List<Map<String, ? super Object>> addLaunchers =
   109             List<Map<String, ? super Object>> addLaunchers =
   117                 ADD_LAUNCHERS.fetchFrom(params);
   110                 ADD_LAUNCHERS.fetchFrom(params);
   118 
   111 
   119             for (int i = 0; i < addLaunchers.size(); i++) {
   112             for (int i = 0; i < addLaunchers.size(); i++) {
   120                 Map<String, ? super Object> sl = addLaunchers.get(i);
   113                 Map<String, ? super Object> sl = addLaunchers.get(i);
   121                 xml.writeStartElement("add-launcher");
   114                 xml.writeStartElement("add-launcher");
   122                 xml.writeCharacters(APP_NAME.fetchFrom(sl));
   115                 xml.writeCharacters(APP_NAME.fetchFrom(sl));
   123                 xml.writeEndElement();
   116                 xml.writeEndElement();
   124             }
   117             }
   125             
   118         });
   126             xml.writeEndElement();
   119     }
   127             xml.writeEndDocument();
   120 
   128             xml.flush();
   121     /**
   129             xml.close();
   122      * Loads application image info from application image.
   130 
   123      * @param appImageDir - path to application image
   131         } catch (XMLStreamException ex) {
   124      * @return valid info about application image or null
   132             Log.verbose(ex);
   125      * @throws IOException
   133             throw new IOException(ex);
   126      */
   134         }
       
   135     }
       
   136 
       
   137     static AppImageFile load(Path appImageDir) throws IOException {
   127     static AppImageFile load(Path appImageDir) throws IOException {
   138         try {
   128         try {
   139             Path path = appImageDir.resolve(XML_FILENAME);
   129             Path path = appImageDir.resolve(FILENAME);
   140             DocumentBuilderFactory dbf =
   130             DocumentBuilderFactory dbf =
   141                     DocumentBuilderFactory.newDefaultInstance();
   131                     DocumentBuilderFactory.newDefaultInstance();
   142             dbf.setFeature(
   132             dbf.setFeature(
   143                    "http://apache.org/xml/features/nonvalidating/load-external-dtd",
   133                    "http://apache.org/xml/features/nonvalidating/load-external-dtd",
   144                     false);
   134                     false);
   145             DocumentBuilder b = dbf.newDocumentBuilder();
   135             DocumentBuilder b = dbf.newDocumentBuilder();
   146             Document doc = b.parse(new FileInputStream(path.toFile()));
   136             Document doc = b.parse(new FileInputStream(path.toFile()));
   147 
   137 
   148             XPath xPath = XPathFactory.newInstance().newXPath();
   138             XPath xPath = XPathFactory.newInstance().newXPath();
   149             
   139 
   150             String mainLauncher = xpathQueryNullable(xPath,
   140             String mainLauncher = xpathQueryNullable(xPath,
   151                     "/jpackage-state/main-launcher/text()", doc);
   141                     "/jpackage-state/main-launcher/text()", doc);
   152             if (mainLauncher == null) {
   142             if (mainLauncher == null) {
   153                 // No main launcher, this is fatal.
   143                 // No main launcher, this is fatal.
   154                 return new AppImageFile();
   144                 return new AppImageFile();
   159             String platform = xpathQueryNullable(xPath,
   149             String platform = xpathQueryNullable(xPath,
   160                     "/jpackage-state/@platform", doc);
   150                     "/jpackage-state/@platform", doc);
   161 
   151 
   162             String version = xpathQueryNullable(xPath,
   152             String version = xpathQueryNullable(xPath,
   163                     "/jpackage-state/@version", doc);
   153                     "/jpackage-state/@version", doc);
   164             
   154 
   165             NodeList launcherNameNodes = (NodeList) xPath.evaluate(
   155             NodeList launcherNameNodes = (NodeList) xPath.evaluate(
   166                     "/jpackage-state/add-launcher/text()", doc,
   156                     "/jpackage-state/add-launcher/text()", doc,
   167                     XPathConstants.NODESET);
   157                     XPathConstants.NODESET);
   168 
   158 
   169             for (int i = 0; i != launcherNameNodes.getLength(); i++) {
   159             for (int i = 0; i != launcherNameNodes.getLength(); i++) {
   170                 addLaunchers.add(launcherNameNodes.item(i).getNodeValue());
   160                 addLaunchers.add(launcherNameNodes.item(i).getNodeValue());
   171             }
   161             }
   172             
   162 
   173             AppImageFile file = new AppImageFile(
   163             AppImageFile file = new AppImageFile(
   174                     mainLauncher, addLaunchers, version, platform);
   164                     mainLauncher, addLaunchers, version, platform);
   175             if (!file.isValid()) {
   165             if (!file.isValid()) {
   176                 file = new AppImageFile();
   166                 file = new AppImageFile();
   177             }
   167             }
   178             return file;       
   168             return file;
   179         } catch (ParserConfigurationException | SAXException ex) {
   169         } catch (ParserConfigurationException | SAXException ex) {
   180             // Let caller sort this out
   170             // Let caller sort this out
   181             throw new IOException(ex);
   171             throw new IOException(ex);
   182         } catch (XPathExpressionException ex) {
   172         } catch (XPathExpressionException ex) {
   183             // This should never happen as XPath expressions should be correct
   173             // This should never happen as XPath expressions should be correct
   184             throw new RuntimeException(ex);
   174             throw new RuntimeException(ex);
   185         }
   175         }
   186     }
   176     }
   187     
   177 
       
   178     /**
       
   179      * Returns list of launcher names configured for the application.
       
   180      * The first item in the returned list is man launcher name.
       
   181      * Following items in the list are names of additional launchers.
       
   182      */
       
   183     static List<String> getLauncherNames(Path appImageDir,
       
   184             Map<String, ? super Object> params) {
       
   185         List<String> launchers = new ArrayList<>();
       
   186         try {
       
   187             AppImageFile appImageInfo = AppImageFile.load(appImageDir);
       
   188             if (appImageInfo != null) {
       
   189                 launchers.add(appImageInfo.getLauncherName());
       
   190                 launchers.addAll(appImageInfo.getAddLauncherNames());
       
   191                 return launchers;
       
   192             }
       
   193         } catch (IOException ioe) {
       
   194             Log.verbose(ioe);
       
   195         }
       
   196 
       
   197         launchers.add(APP_NAME.fetchFrom(params));
       
   198         ADD_LAUNCHERS.fetchFrom(params).stream().map(APP_NAME::fetchFrom).forEach(
       
   199                 launchers::add);
       
   200         return launchers;
       
   201     }
       
   202 
   188     private static String xpathQueryNullable(XPath xPath, String xpathExpr,
   203     private static String xpathQueryNullable(XPath xPath, String xpathExpr,
   189             Document xml) throws XPathExpressionException {
   204             Document xml) throws XPathExpressionException {
   190         NodeList nodes = (NodeList) xPath.evaluate(xpathExpr, xml,
   205         NodeList nodes = (NodeList) xPath.evaluate(xpathExpr, xml,
   191                 XPathConstants.NODESET);
   206                 XPathConstants.NODESET);
   192         if (nodes != null && nodes.getLength() > 0) {
   207         if (nodes != null && nodes.getLength() > 0) {
   196     }
   211     }
   197 
   212 
   198     private static String getVersion() {
   213     private static String getVersion() {
   199         return System.getProperty("java.version");
   214         return System.getProperty("java.version");
   200     }
   215     }
   201     
   216 
   202     private static String getPlatform() {
   217     private static String getPlatform() {
   203         return PLATFORM_LABELS.get(Platform.getPlatform());
   218         return PLATFORM_LABELS.get(Platform.getPlatform());
   204     }
   219     }
   205     
   220 
   206     private boolean isValid() {
   221     private boolean isValid() {
   207         if (launcherName == null || launcherName.length() == 0 ||
   222         if (launcherName == null || launcherName.length() == 0 ||
   208             addLauncherNames.indexOf("") != -1) {
   223             addLauncherNames.indexOf("") != -1) {
   209             // Some launchers have empty names. This is invalid.
   224             // Some launchers have empty names. This is invalid.
   210             return false;
   225             return false;
   211         }
   226         }
   212         
   227 
   213         // Add more validation.
   228         // Add more validation.
   214         
   229 
   215         return true;
   230         return true;
   216     }
   231     }
   217     
   232 
   218 }
   233 }