jdk/src/share/classes/com/sun/servicetag/Installer.java
changeset 16802 ea3325542aa8
parent 16801 e2de240b437f
parent 16575 d7ad0dfaa411
child 16803 3bdc22a32b0e
equal deleted inserted replaced
16801:e2de240b437f 16802:ea3325542aa8
     1 /*
       
     2  * Copyright (c) 2008, 2011, 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  */
       
    25 
       
    26 package com.sun.servicetag;
       
    27 
       
    28 import java.io.*;
       
    29 import java.util.HashSet;
       
    30 import java.util.Locale;
       
    31 import java.util.Properties;
       
    32 import java.util.Set;
       
    33 import java.util.List;
       
    34 import java.util.ArrayList;
       
    35 import static com.sun.servicetag.Util.*;
       
    36 
       
    37 /**
       
    38  * Service Tag Installer for Java SE.
       
    39  */
       
    40 public class Installer {
       
    41     // System properties for testing
       
    42     private static String SVCTAG_DIR_PATH =
       
    43         "servicetag.dir.path";
       
    44     private static String SVCTAG_ENABLE_REGISTRATION =
       
    45         "servicetag.registration.enabled";
       
    46     private final static String ORACLE = "Oracle";
       
    47     private final static String SUN = "Sun Microsystems";
       
    48     private final static String REGISTRATION_XML = "registration.xml";
       
    49     private final static String SERVICE_TAG_FILE = "servicetag";
       
    50     private final static String REGISTRATION_HTML_NAME = "register";
       
    51 
       
    52     private final static Locale[] knownSupportedLocales =
       
    53         new Locale[] { Locale.ENGLISH,
       
    54                        Locale.JAPANESE,
       
    55                        Locale.SIMPLIFIED_CHINESE};
       
    56 
       
    57     private final static String javaHome = System.getProperty("java.home");
       
    58     private static File svcTagDir;
       
    59     private static File serviceTagFile;
       
    60     private static File regXmlFile;
       
    61     private static RegistrationData registration;
       
    62     private static boolean supportRegistration;
       
    63     private static String registerHtmlParent;
       
    64     private static Set<Locale> supportedLocales = new HashSet<>();
       
    65     private static Properties svcTagProps = null;
       
    66     private static String[] jreArchs = null;
       
    67     static {
       
    68         String dir = System.getProperty(SVCTAG_DIR_PATH);
       
    69         if (dir == null) {
       
    70             svcTagDir = new File(getJrePath(), "lib" + File.separator + SERVICE_TAG_FILE);
       
    71         } else {
       
    72             svcTagDir = new File(dir);
       
    73         }
       
    74         serviceTagFile = new File(svcTagDir, SERVICE_TAG_FILE);
       
    75         regXmlFile = new File(svcTagDir, REGISTRATION_XML);
       
    76         if (System.getProperty(SVCTAG_ENABLE_REGISTRATION) == null) {
       
    77             supportRegistration = isJdk();
       
    78         } else {
       
    79             supportRegistration = true;
       
    80         }
       
    81     }
       
    82 
       
    83     private Installer() {
       
    84     }
       
    85 
       
    86     // Implementation of ServiceTag.getJavaServiceTag(String) method
       
    87     static ServiceTag getJavaServiceTag(String source) throws IOException {
       
    88         String vendor = System.getProperty("java.vendor", "");
       
    89         if (!vendor.startsWith(SUN) && !vendor.startsWith(ORACLE)) {
       
    90             // Products bundling this implementation may run on
       
    91             // Mac OS which is not a Sun/Oracle JDK
       
    92             return null;
       
    93         }
       
    94         boolean cleanup = false;
       
    95         try {
       
    96             // Check if we have the swordfish entries for this JRE version
       
    97             if (loadServiceTagProps() == null) {
       
    98                 return null;
       
    99             }
       
   100 
       
   101             ServiceTag st = getJavaServiceTag();
       
   102             // Check if the service tag created by this bundle owner
       
   103             if (st != null && st.getSource().equals(source)) {
       
   104                 // Install the system service tag if supported
       
   105                 // stclient may be installed after the service tag creation
       
   106                 if (Registry.isSupported()) {
       
   107                     installSystemServiceTag();
       
   108                 }
       
   109                 return st;
       
   110             }
       
   111 
       
   112             // in case any exception thrown during the cleanup
       
   113             cleanup = true;
       
   114 
       
   115             // re-create a new one for this bundle owner
       
   116             // first delete the registration data
       
   117             deleteRegistrationData();
       
   118             cleanup = false;
       
   119 
       
   120             // create service tag and generate new register.html pages
       
   121             return createServiceTag(source);
       
   122         } finally {
       
   123             if (cleanup) {
       
   124                 if (regXmlFile.exists()) {
       
   125                     regXmlFile.delete();
       
   126                 }
       
   127                 if (serviceTagFile.exists()) {
       
   128                     serviceTagFile.delete();
       
   129                 }
       
   130             }
       
   131         }
       
   132     }
       
   133 
       
   134     /**
       
   135      * Returns the Java SE registration data located in
       
   136      * the <JRE>/lib/servicetag/registration.xml by default.
       
   137      *
       
   138      * @throws IllegalArgumentException if the registration data
       
   139      *         is of invalid format.
       
   140      */
       
   141     private static synchronized RegistrationData getRegistrationData()
       
   142             throws IOException {
       
   143         if (registration != null) {
       
   144             return registration;
       
   145         }
       
   146         if (regXmlFile.exists()) {
       
   147             try (BufferedInputStream in =
       
   148                     new BufferedInputStream(new FileInputStream(regXmlFile)))
       
   149             {
       
   150                 registration = RegistrationData.loadFromXML(in);
       
   151             } catch (IllegalArgumentException ex) {
       
   152                 System.err.println("Error: Bad registration data \"" +
       
   153                                     regXmlFile + "\":" + ex.getMessage());
       
   154                 throw ex;
       
   155             }
       
   156         } else {
       
   157             registration = new RegistrationData();
       
   158         }
       
   159         return registration;
       
   160     }
       
   161 
       
   162     /**
       
   163      * Write the registration data to the registration.xml file.
       
   164      *
       
   165      * The offline registration page has to be regenerated with
       
   166      * the new registration data.
       
   167      *
       
   168      * @throws java.io.IOException
       
   169      */
       
   170     private static synchronized void writeRegistrationXml()
       
   171             throws IOException {
       
   172         if (!svcTagDir.exists()) {
       
   173             // This check is for NetBeans or other products that
       
   174             // bundles this com.sun.servicetag implementation for
       
   175             // pre-6u5 release.
       
   176             if (!svcTagDir.mkdir()) {
       
   177                 throw new IOException("Failed to create directory: " + svcTagDir);
       
   178             }
       
   179         }
       
   180 
       
   181         // regenerate the new offline registration page
       
   182         deleteRegistrationHtmlPage();
       
   183         getRegistrationHtmlPage();
       
   184 
       
   185         try (BufferedOutputStream out =
       
   186                 new BufferedOutputStream(new FileOutputStream(regXmlFile)))
       
   187         {
       
   188             getRegistrationData().storeToXML(out);
       
   189         } catch (IllegalArgumentException ex) {
       
   190             System.err.println("Error: Bad registration data \"" +
       
   191                                 regXmlFile + "\":" + ex.getMessage());
       
   192             throw ex;
       
   193         }
       
   194     }
       
   195 
       
   196     /**
       
   197      * Returns the instance urn(s) stored in the servicetag file
       
   198      * or empty set if file not exists.
       
   199      */
       
   200     private static Set<String> getInstalledURNs() throws IOException {
       
   201         Set<String> urnSet = new HashSet<>();
       
   202         if (serviceTagFile.exists()) {
       
   203             try (BufferedReader in = new BufferedReader(new FileReader(serviceTagFile))) {
       
   204                 String urn;
       
   205                 while ((urn = in.readLine()) != null) {
       
   206                     urn = urn.trim();
       
   207                     if (urn.length() > 0) {
       
   208                         urnSet.add(urn);
       
   209                     }
       
   210                 }
       
   211             }
       
   212         }
       
   213         return urnSet;
       
   214     }
       
   215 
       
   216     /**
       
   217      * Return the Java SE service tag(s) if it exists.
       
   218      * Typically only one Java SE service tag but it could have two for
       
   219      * Solaris 32-bit and 64-bit on the same install directory.
       
   220      *
       
   221      * @return the service tag(s) for Java SE
       
   222      */
       
   223     private static ServiceTag[] getJavaServiceTagArray() throws IOException {
       
   224         RegistrationData regData = getRegistrationData();
       
   225         Set<ServiceTag> svcTags = regData.getServiceTags();
       
   226         Set<ServiceTag> result = new HashSet<>();
       
   227 
       
   228         Properties props = loadServiceTagProps();
       
   229         String jdkUrn = props.getProperty("servicetag.jdk.urn");
       
   230         String jreUrn = props.getProperty("servicetag.jre.urn");
       
   231         for (ServiceTag st : svcTags) {
       
   232             if (st.getProductURN().equals(jdkUrn) ||
       
   233                 st.getProductURN().equals(jreUrn)) {
       
   234                 result.add(st);
       
   235             }
       
   236         }
       
   237         return result.toArray(new ServiceTag[0]);
       
   238     }
       
   239 
       
   240     /**
       
   241      * Returns the Java SE service tag for this running platform;
       
   242      * or null if not exist.
       
   243      * This method will return the 64-bit service tag if the JDK
       
   244      * supports both 32-bit and 64-bit if already created.
       
   245      */
       
   246     private static ServiceTag getJavaServiceTag() throws IOException {
       
   247         String definedId = getProductDefinedId();
       
   248         for (ServiceTag st : getJavaServiceTagArray()) {
       
   249             if (st.getProductDefinedInstanceID().equals(definedId)) {
       
   250                 return st;
       
   251             }
       
   252         }
       
   253         return null;
       
   254     }
       
   255 
       
   256     /**
       
   257      * Create a service tag for Java SE and install in the system
       
   258      * service tag registry if supported.
       
   259      *
       
   260      * A registration data <JRE>/lib/servicetag/registration.xml
       
   261      * will be created to storeToXML the XML entry for Java SE service tag.
       
   262      * If the system supports service tags, this method will install
       
   263      * the Java SE service tag in the system service tag registry and
       
   264      * its <tt>instance_urn</tt> will be stored to <JRE>/lib/servicetag/servicetag.
       
   265      *
       
   266      * If <JRE>/lib/servicetag/registration.xml exists but is not installed
       
   267      * in the system service tag registry (i.e. servicetag doesn't exist),
       
   268      * this method will install it as described above.
       
   269      *
       
   270      * If the system supports service tag, stclient will be used
       
   271      * to create the Java SE service tag.
       
   272      *
       
   273      * A Solaris 32-bit and 64-bit JDK will be installed in the same
       
   274      * directory but the registration.xml will have 2 service tags.
       
   275      * The servicetag file will also contain 2 instance_urns for that case.
       
   276      */
       
   277     private static ServiceTag createServiceTag(String svcTagSource)
       
   278             throws IOException {
       
   279         // determine if a new service tag is needed to be created
       
   280         ServiceTag newSvcTag = null;
       
   281         if (getJavaServiceTag() == null) {
       
   282             newSvcTag = newServiceTag(svcTagSource);
       
   283         }
       
   284 
       
   285         // Add the new service tag in the registration data
       
   286         if (newSvcTag != null) {
       
   287             RegistrationData regData = getRegistrationData();
       
   288 
       
   289             // Add the service tag to the registration data in JDK/JRE
       
   290             newSvcTag = regData.addServiceTag(newSvcTag);
       
   291 
       
   292             // add if there is a service tag for the OS
       
   293             ServiceTag osTag = SolarisServiceTag.getServiceTag();
       
   294             if (osTag != null && regData.getServiceTag(osTag.getInstanceURN()) == null) {
       
   295                 regData.addServiceTag(osTag);
       
   296             }
       
   297             // write to the registration.xml
       
   298             writeRegistrationXml();
       
   299         }
       
   300 
       
   301         // Install the system service tag if supported
       
   302         if (Registry.isSupported()) {
       
   303             installSystemServiceTag();
       
   304         }
       
   305         return newSvcTag;
       
   306     }
       
   307 
       
   308     private static void installSystemServiceTag() throws IOException {
       
   309         // only install the service tag in the registry if
       
   310         // it has permission to write the servicetag file.
       
   311         if ((!serviceTagFile.exists() && !svcTagDir.canWrite()) ||
       
   312                 (serviceTagFile.exists() && !serviceTagFile.canWrite())) {
       
   313             return;
       
   314         }
       
   315 
       
   316         Set<String> urns = getInstalledURNs();
       
   317         ServiceTag[] javaSvcTags = getJavaServiceTagArray();
       
   318         if (urns.size() < javaSvcTags.length) {
       
   319             for (ServiceTag st : javaSvcTags) {
       
   320                 // Add the service tag in the system service tag registry
       
   321                 // if not installed
       
   322                 String instanceURN = st.getInstanceURN();
       
   323                 if (!urns.contains(instanceURN)) {
       
   324                     Registry.getSystemRegistry().addServiceTag(st);
       
   325                 }
       
   326             }
       
   327         }
       
   328         writeInstalledUrns();
       
   329     }
       
   330 
       
   331     private static ServiceTag newServiceTag(String svcTagSource) throws IOException {
       
   332         Properties props = loadServiceTagProps();
       
   333 
       
   334         // Determine the product URN and name
       
   335         String productURN;
       
   336         String productName;
       
   337 
       
   338         if (isJdk()) {
       
   339             // <HOME>/jre exists which implies it's a JDK
       
   340             productURN = props.getProperty("servicetag.jdk.urn");
       
   341             productName = props.getProperty("servicetag.jdk.name");
       
   342         } else {
       
   343             // Otherwise, it's a JRE
       
   344             productURN = props.getProperty("servicetag.jre.urn");
       
   345             productName = props.getProperty("servicetag.jre.name");
       
   346         }
       
   347 
       
   348         return ServiceTag.newInstance(ServiceTag.generateInstanceURN(),
       
   349                                       productName,
       
   350                                       System.getProperty("java.version"),
       
   351                                       productURN,
       
   352                                       props.getProperty("servicetag.parent.name"),
       
   353                                       props.getProperty("servicetag.parent.urn"),
       
   354                                       getProductDefinedId(),
       
   355                                       System.getProperty("java.vendor"),
       
   356                                       System.getProperty("os.arch"),
       
   357                                       getZoneName(),
       
   358                                       svcTagSource);
       
   359     }
       
   360 
       
   361     /**
       
   362      * Delete the registration data, the offline registration pages and
       
   363      * the service tags in the system service tag registry if installed.
       
   364      *
       
   365      * The registration.xml and servicetag file will be removed.
       
   366      */
       
   367     private static synchronized void deleteRegistrationData()
       
   368             throws IOException {
       
   369         try {
       
   370             // delete the offline registration page
       
   371             deleteRegistrationHtmlPage();
       
   372 
       
   373             // Remove the service tag from the system ST registry if exists
       
   374             Set<String> urns = getInstalledURNs();
       
   375             if (urns.size() > 0 && Registry.isSupported()) {
       
   376                 for (String u : urns) {
       
   377                     Registry.getSystemRegistry().removeServiceTag(u);
       
   378                 }
       
   379             }
       
   380             registration = null;
       
   381         } finally {
       
   382             // Delete the registration.xml and servicetag files if exists
       
   383             if (regXmlFile.exists()) {
       
   384                 if (!regXmlFile.delete()) {
       
   385                     throw new IOException("Failed to delete " + regXmlFile);
       
   386                 }
       
   387             }
       
   388             if (serviceTagFile.exists()) {
       
   389                 if (!serviceTagFile.delete()) {
       
   390                     throw new IOException("Failed to delete " + serviceTagFile);
       
   391                 }
       
   392             }
       
   393         }
       
   394     }
       
   395 
       
   396     /**
       
   397      * Updates the registration data to contain one single service tag
       
   398      * for the running Java runtime.
       
   399      */
       
   400     private static synchronized void updateRegistrationData(String svcTagSource)
       
   401             throws IOException {
       
   402         RegistrationData regData = getRegistrationData();
       
   403         ServiceTag curSvcTag = newServiceTag(svcTagSource);
       
   404 
       
   405         ServiceTag[] javaSvcTags = getJavaServiceTagArray();
       
   406         Set<String> urns = getInstalledURNs();
       
   407         for (ServiceTag st : javaSvcTags) {
       
   408             if (!st.getProductDefinedInstanceID().equals(curSvcTag.getProductDefinedInstanceID())) {
       
   409                 String instanceURN = st.getInstanceURN();
       
   410                 regData.removeServiceTag(instanceURN);
       
   411 
       
   412                 // remove it from the system service tag registry if exists
       
   413                 if (urns.contains(instanceURN) && Registry.isSupported()) {
       
   414                     Registry.getSystemRegistry().removeServiceTag(instanceURN);
       
   415                 }
       
   416             }
       
   417         }
       
   418         writeRegistrationXml();
       
   419         writeInstalledUrns();
       
   420     }
       
   421 
       
   422     private static void writeInstalledUrns() throws IOException {
       
   423         // if the Registry is not supported,
       
   424         // remove the servicetag file
       
   425         if (!Registry.isSupported() && serviceTagFile.exists()) {
       
   426             serviceTagFile.delete();
       
   427             return;
       
   428         }
       
   429 
       
   430         try (PrintWriter out = new PrintWriter(serviceTagFile)) {
       
   431             ServiceTag[] javaSvcTags = getJavaServiceTagArray();
       
   432             for (ServiceTag st : javaSvcTags) {
       
   433                 // Write the instance_run to the servicetag file
       
   434                 String instanceURN = st.getInstanceURN();
       
   435                 out.println(instanceURN);
       
   436             }
       
   437         }
       
   438     }
       
   439 
       
   440     /**
       
   441      * Load the properties for generating Java SE service tags.
       
   442      *
       
   443      * @param version Version of Java SE
       
   444      */
       
   445     private static synchronized Properties loadServiceTagProps() throws IOException {
       
   446         if (svcTagProps != null) {
       
   447             return svcTagProps;
       
   448         }
       
   449 
       
   450         // For Java SE 8 and later releases, JDK and JRE both use
       
   451         // the same product number.  The sworRDFish metadata were
       
   452         // for legacy Sun part number.
       
   453         String filename = "/com/sun/servicetag/resources/javase_servicetag.properties";
       
   454         try (InputStream in = Installer.class.getResourceAsStream(filename)) {
       
   455             svcTagProps = new Properties();
       
   456             svcTagProps.load(in);
       
   457         }
       
   458         return svcTagProps;
       
   459     }
       
   460 
       
   461     /**
       
   462      * Returns the product defined instance ID for Java SE.
       
   463      * It is a list of comma-separated name/value pairs:
       
   464      *    "id=<full-version>  <arch> [<arch>]*"
       
   465      *    "dir=<java.home system property value>"
       
   466      *
       
   467      * where <full-version> is the full version string of the JRE,
       
   468      *       <arch> is the architecture that the runtime supports
       
   469      *       (i.e. "sparc", "sparcv9", "i386", "amd64" (ISA list))
       
   470      *
       
   471      * For Solaris, it can be dual mode that can support both
       
   472      * 32-bit and 64-bit. the "id" will be set to
       
   473      *     "1.6.0_03-b02 sparc sparcv9"
       
   474      *
       
   475      * The "dir" property is included in the service tag to enable
       
   476      * the Service Tag software to determine if a service tag for
       
   477      * Java SE is invalid and perform appropriate service tag
       
   478      * cleanup if necessary.  See RFE# 6574781 Service Tags Enhancement.
       
   479      *
       
   480      */
       
   481     private static String getProductDefinedId() {
       
   482         StringBuilder definedId = new StringBuilder();
       
   483         definedId.append("id=");
       
   484         definedId.append(System.getProperty("java.runtime.version"));
       
   485 
       
   486         String[] archs = getJreArchs();
       
   487         for (String name : archs) {
       
   488             definedId.append(" " + name);
       
   489         }
       
   490 
       
   491         String location = ",dir=" + javaHome;
       
   492         if ((definedId.length() + location.length()) < 256) {
       
   493             definedId.append(",dir=");
       
   494             definedId.append(javaHome);
       
   495         } else {
       
   496             // if it exceeds the limit, we will not include the location
       
   497             if (isVerbose()) {
       
   498                 System.err.println("Warning: Product defined instance ID exceeds the field limit:");
       
   499             }
       
   500         }
       
   501 
       
   502         return definedId.toString();
       
   503     }
       
   504 
       
   505     /**
       
   506      * Returns the architectures that the runtime supports
       
   507      *  (i.e. "sparc", "sparcv9", "i386", "amd64" (ISA list))
       
   508      * The directory name where libjava.so is located.
       
   509      *
       
   510      * On Windows, returns the "os.arch" system property value.
       
   511      */
       
   512     private synchronized static String[] getJreArchs() {
       
   513         if (jreArchs != null) {
       
   514             return jreArchs;
       
   515         }
       
   516 
       
   517         Set<String> archs = new HashSet<>();
       
   518 
       
   519         String os = System.getProperty("os.name");
       
   520         if (os.equals("SunOS") || os.equals("Linux")) {
       
   521             // Traverse the directories under <JRE>/lib.
       
   522             // If <JRE>/lib/<arch>/libjava.so exists, add <arch>
       
   523             // to the product defined ID
       
   524             File dir = new File(getJrePath() + File.separator + "lib");
       
   525             if (dir.isDirectory()) {
       
   526                 String[] children = dir.list();
       
   527                 for (String name : children) {
       
   528                     File f = new File(dir, name + File.separator + "libjava.so");
       
   529                     if (f.exists()) {
       
   530                         archs.add(name);
       
   531                     }
       
   532                 }
       
   533             }
       
   534         } else {
       
   535             // Windows - append the os.arch
       
   536             archs.add(System.getProperty("os.arch"));
       
   537         }
       
   538         jreArchs = archs.toArray(new String[0]);
       
   539         return jreArchs;
       
   540     }
       
   541 
       
   542     /**
       
   543      * Return the zonename if zone is supported; otherwise, return
       
   544      * "global".
       
   545      */
       
   546     private static String getZoneName() throws IOException {
       
   547         String zonename = "global";
       
   548 
       
   549         String command = "/usr/bin/zonename";
       
   550         File f = new File(command);
       
   551         // com.sun.servicetag package has to be compiled with JDK 5 as well
       
   552         // JDK 5 doesn't support the File.canExecute() method.
       
   553         // Risk not checking isExecute() for the zonename command is very low.
       
   554         if (f.exists()) {
       
   555             ProcessBuilder pb = new ProcessBuilder(command);
       
   556             Process p = pb.start();
       
   557             String output = commandOutput(p);
       
   558             if (p.exitValue() == 0) {
       
   559                 zonename = output.trim();
       
   560             }
       
   561 
       
   562         }
       
   563         return zonename;
       
   564     }
       
   565 
       
   566     private synchronized static String getRegisterHtmlParent() throws IOException {
       
   567         if (registerHtmlParent == null) {
       
   568             File htmlDir;    // register.html is put under the JDK directory
       
   569             if (getJrePath().endsWith(File.separator + "jre")) {
       
   570                 htmlDir = new File(getJrePath(), "..");
       
   571             } else {
       
   572                 // j2se non-image build
       
   573                 htmlDir = new File(getJrePath());
       
   574             }
       
   575 
       
   576             // initialize the supported locales
       
   577             initSupportedLocales(htmlDir);
       
   578 
       
   579             // Determine the location of the offline registration page
       
   580             String path = System.getProperty(SVCTAG_DIR_PATH);
       
   581             if (path == null) {
       
   582                 // Default is <JDK>/register.html
       
   583                 registerHtmlParent = htmlDir.getCanonicalPath();
       
   584             } else {
       
   585                 File f = new File(path);
       
   586                 registerHtmlParent = f.getCanonicalPath();
       
   587                 if (!f.isDirectory()) {
       
   588                     throw new InternalError("Path " + path + " set in \"" +
       
   589                             SVCTAG_DIR_PATH + "\" property is not a directory");
       
   590                 }
       
   591             }
       
   592         }
       
   593         return registerHtmlParent;
       
   594     }
       
   595 
       
   596     /**
       
   597      * Returns the File object of the offline registration page localized
       
   598      * for the default locale in the JDK directory.
       
   599      */
       
   600     static synchronized File getRegistrationHtmlPage() throws IOException {
       
   601         if (!supportRegistration) {
       
   602             // No register.html page generated if JRE
       
   603             return null;
       
   604         }
       
   605 
       
   606         String parent = getRegisterHtmlParent();
       
   607 
       
   608         // check if the offline registration page is already generated
       
   609         File f = new File(parent, REGISTRATION_HTML_NAME + ".html");
       
   610         if (!f.exists()) {
       
   611             // Generate the localized version of the offline registration Page
       
   612             generateRegisterHtml(parent);
       
   613         }
       
   614 
       
   615         String name = REGISTRATION_HTML_NAME;
       
   616         Locale locale = getDefaultLocale();
       
   617         if (!locale.equals(Locale.ENGLISH) && supportedLocales.contains(locale)) {
       
   618             // if the locale is not English and is supported by JDK
       
   619             // set to the appropriate offline registration page;
       
   620             // otherwise,set to register.html.
       
   621             name = REGISTRATION_HTML_NAME + "_" + locale.toString();
       
   622         }
       
   623         File htmlFile = new File(parent, name + ".html");
       
   624         if (isVerbose()) {
       
   625             System.out.print("Offline registration page: " + htmlFile);
       
   626             System.out.println((htmlFile.exists() ?
       
   627                                "" : " not exist. Use register.html"));
       
   628         }
       
   629         if (htmlFile.exists()) {
       
   630             return htmlFile;
       
   631         } else {
       
   632             return new File(parent,
       
   633                             REGISTRATION_HTML_NAME + ".html");
       
   634         }
       
   635     }
       
   636 
       
   637     private static Locale getDefaultLocale() {
       
   638         List<Locale> candidateLocales = getCandidateLocales(Locale.getDefault());
       
   639         for (Locale l : candidateLocales) {
       
   640             if (supportedLocales.contains(l)) {
       
   641                 return l;
       
   642             }
       
   643         }
       
   644         return Locale.getDefault();
       
   645     }
       
   646 
       
   647     private static List<Locale> getCandidateLocales(Locale locale) {
       
   648         String language = locale.getLanguage();
       
   649         String country = locale.getCountry();
       
   650         String variant = locale.getVariant();
       
   651 
       
   652         List<Locale> locales = new ArrayList<>(3);
       
   653         if (variant.length() > 0) {
       
   654             locales.add(locale);
       
   655         }
       
   656         if (country.length() > 0) {
       
   657             locales.add((locales.isEmpty()) ?
       
   658                         locale : new Locale(language, country, ""));
       
   659         }
       
   660         if (language.length() > 0) {
       
   661             locales.add((locales.isEmpty()) ?
       
   662                         locale : new Locale(language, "", ""));
       
   663         }
       
   664         return locales;
       
   665     }
       
   666 
       
   667     // Remove the offline registration pages
       
   668     private static void deleteRegistrationHtmlPage() throws IOException {
       
   669         String parent = getRegisterHtmlParent();
       
   670         if (parent == null) {
       
   671             return;
       
   672         }
       
   673 
       
   674         for (Locale locale : supportedLocales) {
       
   675             String name = REGISTRATION_HTML_NAME;
       
   676             if (!locale.equals(Locale.ENGLISH)) {
       
   677                 name += "_" + locale.toString();
       
   678             }
       
   679             File f = new File(parent, name + ".html");
       
   680             if (f.exists()) {
       
   681                 if (!f.delete()) {
       
   682                     throw new IOException("Failed to delete " + f);
       
   683                 }
       
   684             }
       
   685         }
       
   686     }
       
   687 
       
   688     private static void initSupportedLocales(File jdkDir) {
       
   689         if (supportedLocales.isEmpty()) {
       
   690             // initialize with the known supported locales
       
   691             for (Locale l : knownSupportedLocales) {
       
   692                 supportedLocales.add(l);
       
   693             }
       
   694         }
       
   695 
       
   696         // Determine unknown supported locales if any
       
   697         // by finding the localized version of README.html
       
   698         // This prepares if a new locale in JDK is supported in
       
   699         // e.g. in the OpenSource world
       
   700         FilenameFilter ff = new FilenameFilter() {
       
   701             public boolean accept(File dir, String name) {
       
   702                 String fname = name.toLowerCase();
       
   703                 if (fname.startsWith("readme") && fname.endsWith(".html")) {
       
   704                     return true;
       
   705                 }
       
   706                 return false;
       
   707             }
       
   708         };
       
   709 
       
   710         String[] readmes = jdkDir.list(ff);
       
   711         for (String name : readmes) {
       
   712             String basename = name.substring(0, name.length() - ".html".length());
       
   713             String[] ss = basename.split("_");
       
   714             switch (ss.length) {
       
   715                 case 1:
       
   716                     // English version
       
   717                     break;
       
   718                 case 2:
       
   719                     supportedLocales.add(new Locale(ss[1]));
       
   720                     break;
       
   721                 case 3:
       
   722                     supportedLocales.add(new Locale(ss[1], ss[2]));
       
   723                     break;
       
   724                 default:
       
   725                     // ignore
       
   726                     break;
       
   727             }
       
   728         }
       
   729         if (isVerbose()) {
       
   730             System.out.println("Supported locales: ");
       
   731             for (Locale l : supportedLocales) {
       
   732                 System.out.println(l);
       
   733             }
       
   734         }
       
   735     }
       
   736 
       
   737     private static final String JDK_HEADER_PNG_KEY = "@@JDK_HEADER_PNG@@";
       
   738     private static final String JDK_VERSION_KEY = "@@JDK_VERSION@@";
       
   739     private static final String REGISTRATION_URL_KEY = "@@REGISTRATION_URL@@";
       
   740     private static final String REGISTRATION_PAYLOAD_KEY = "@@REGISTRATION_PAYLOAD@@";
       
   741 
       
   742     @SuppressWarnings("unchecked")
       
   743     private static void generateRegisterHtml(String parent) throws IOException {
       
   744         int version = Util.getJdkVersion();
       
   745         int update = Util.getUpdateVersion();
       
   746         String jdkVersion = "Version " + version;
       
   747         if (update > 0) {
       
   748             // product name is not translated
       
   749             jdkVersion += " Update " + update;
       
   750         }
       
   751         RegistrationData regData = getRegistrationData();
       
   752         // Make sure it uses the canonical path before getting the URI.
       
   753         File img = new File(svcTagDir.getCanonicalPath(), "jdk_header.png");
       
   754         String headerImageSrc = img.toURI().toString();
       
   755 
       
   756         // Format the registration data in one single line
       
   757         StringBuilder payload = new StringBuilder();
       
   758         String xml = regData.toString().replaceAll("\"", "%22");
       
   759         try (BufferedReader reader = new BufferedReader(new StringReader(xml))) {
       
   760             String line = null;
       
   761             while ((line = reader.readLine()) != null) {
       
   762                 payload.append(line.trim());
       
   763             }
       
   764         }
       
   765 
       
   766         String resourceFilename = "/com/sun/servicetag/resources/register";
       
   767         for (Locale locale : supportedLocales) {
       
   768             String name = REGISTRATION_HTML_NAME;
       
   769             String resource = resourceFilename;
       
   770             if (!locale.equals(Locale.ENGLISH)) {
       
   771                 name += "_" + locale.toString();
       
   772                 resource += "_" + locale.toString();
       
   773             }
       
   774             File f = new File(parent, name + ".html");
       
   775             InputStream in = null;
       
   776             BufferedReader br = null;
       
   777             PrintWriter pw = null;
       
   778             String registerURL = SunConnection.
       
   779                 getRegistrationURL(regData.getRegistrationURN(),
       
   780                                    locale,
       
   781                                    String.valueOf(version)).toString();
       
   782             try {
       
   783                 in = Installer.class.getResourceAsStream(resource + ".html");
       
   784                 if (in == null) {
       
   785                     // if the resource file is missing
       
   786                     if (isVerbose()) {
       
   787                         System.out.println("Missing resouce file: " + resource + ".html");
       
   788                     }
       
   789                     continue;
       
   790                 }
       
   791                 if (isVerbose()) {
       
   792                     System.out.println("Generating " + f + " from " + resource + ".html");
       
   793                 }
       
   794 
       
   795                 try {
       
   796                     br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
       
   797                     pw = new PrintWriter(f, "UTF-8");
       
   798                     String line = null;
       
   799                     while ((line = br.readLine()) != null) {
       
   800                         String output = line;
       
   801                         if (line.contains(JDK_VERSION_KEY)) {
       
   802                             output = line.replace(JDK_VERSION_KEY, jdkVersion);
       
   803                         } else if (line.contains(JDK_HEADER_PNG_KEY)) {
       
   804                             output = line.replace(JDK_HEADER_PNG_KEY, headerImageSrc);
       
   805                         } else if (line.contains(REGISTRATION_URL_KEY)) {
       
   806                             output = line.replace(REGISTRATION_URL_KEY, registerURL);
       
   807                         } else if (line.contains(REGISTRATION_PAYLOAD_KEY)) {
       
   808                             output = line.replace(REGISTRATION_PAYLOAD_KEY, payload.toString());
       
   809                         }
       
   810                         pw.println(output);
       
   811                     }
       
   812                     f.setReadOnly();
       
   813                     pw.flush();
       
   814                 } finally {
       
   815                     // It's safe for this finally block to have two close statements
       
   816                     // consecutively as PrintWriter.close doesn't throw IOException.
       
   817                     if (pw != null) {
       
   818                         pw.close();
       
   819                     }
       
   820                     if (br!= null) {
       
   821                         br.close();
       
   822                     }
       
   823                 }
       
   824             } finally {
       
   825                 if (in != null) {
       
   826                     in.close();
       
   827                 }
       
   828             }
       
   829         }
       
   830     }
       
   831 
       
   832     private static final int MAX_SOURCE_LEN = 63;
       
   833 
       
   834     /**
       
   835      * A utility class to create a service tag for Java SE.
       
   836      * <p>
       
   837      * <b>Usage:</b><br>
       
   838      * <blockquote><tt>
       
   839      * &lt;JAVA_HOME&gt;/bin/java com.sun.servicetag.Installer
       
   840      * </tt></blockquote>
       
   841      * <p>
       
   842      */
       
   843     public static void main(String[] args) {
       
   844         String source = "Manual ";
       
   845         String runtimeName = System.getProperty("java.runtime.name");
       
   846         if (runtimeName.startsWith("OpenJDK")) {
       
   847             source = "OpenJDK ";
       
   848         }
       
   849         source += System.getProperty("java.runtime.version");
       
   850         if (source.length() > MAX_SOURCE_LEN) {
       
   851             source = source.substring(0, MAX_SOURCE_LEN);
       
   852         }
       
   853 
       
   854         // Parse the options (arguments starting with "-" )
       
   855         boolean delete = false;
       
   856         boolean update = false;
       
   857         boolean register = false;
       
   858         int count = 0;
       
   859         while (count < args.length) {
       
   860             String arg = args[count];
       
   861             if (arg.trim().length() == 0) {
       
   862                 // skip empty arguments
       
   863                 count++;
       
   864                 continue;
       
   865             }
       
   866 
       
   867             if (arg.equals("-source")) {
       
   868                 source = args[++count];
       
   869             } else if (arg.equals("-delete")) {
       
   870                 delete = true;
       
   871             } else if (arg.equals("-register")) {
       
   872                 register = true;
       
   873             } else {
       
   874                 usage();
       
   875                 return;
       
   876             }
       
   877             count++;
       
   878         }
       
   879         try {
       
   880             if (delete) {
       
   881                 deleteRegistrationData();
       
   882             } else {
       
   883                 ServiceTag[] javaSvcTags = getJavaServiceTagArray();
       
   884                 String[] archs = getJreArchs();
       
   885                 if (javaSvcTags.length > archs.length) {
       
   886                     // 64-bit has been uninstalled
       
   887                     // so remove the service tag
       
   888                     updateRegistrationData(source);
       
   889                 } else {
       
   890                     // create the service tag
       
   891                     createServiceTag(source);
       
   892                 }
       
   893             }
       
   894 
       
   895             if (register) {
       
   896                 // Registration is only supported by JDK
       
   897                 // For testing purpose, override with a "servicetag.enable.registration" property
       
   898 
       
   899                 RegistrationData regData = getRegistrationData();
       
   900                 if (supportRegistration && !regData.getServiceTags().isEmpty()) {
       
   901                     SunConnection.register(regData,
       
   902                                            getDefaultLocale(),
       
   903                                            String.valueOf(Util.getJdkVersion()));
       
   904                 }
       
   905             }
       
   906             System.exit(0);
       
   907         } catch (IOException e) {
       
   908             System.err.println("I/O Error: " + e.getMessage());
       
   909             if (isVerbose()) {
       
   910                 e.printStackTrace();
       
   911             }
       
   912         } catch (IllegalArgumentException ex) {
       
   913             if (isVerbose()) {
       
   914                 ex.printStackTrace();
       
   915             }
       
   916         } catch (Exception e) {
       
   917             System.err.println("Error: " + e.getMessage());
       
   918             if (isVerbose()) {
       
   919                 e.printStackTrace();
       
   920             }
       
   921         }
       
   922         System.exit(1);
       
   923     }
       
   924 
       
   925     private static void usage() {
       
   926         System.out.println("Usage:");
       
   927         System.out.print("    " + Installer.class.getName());
       
   928         System.out.println(" [-delete|-source <source>|-register]");
       
   929         System.out.println("       to create a service tag for the Java platform");
       
   930         System.out.println("");
       
   931         System.out.println("Internal Options:");
       
   932         System.out.println("    -source: to specify the source of the service tag to be created");
       
   933         System.out.println("    -delete: to delete the service tag ");
       
   934         System.out.println("    -register: to register the JDK");
       
   935         System.out.println("    -help:   to print this help message");
       
   936     }
       
   937 }