jdk/src/share/classes/sun/jkernel/DownloadManager.java
changeset 3111 fefdeafb7ab9
child 3958 b8acd5ee4f4f
equal deleted inserted replaced
2790:e9771c308d06 3111:fefdeafb7ab9
       
     1 /*
       
     2  * Copyright 2008 - 2009 Sun Microsystems, Inc.  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.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 package sun.jkernel;
       
    26 
       
    27 import java.io.*;
       
    28 import java.security.*;
       
    29 import java.util.*;
       
    30 import java.util.concurrent.*;
       
    31 import java.util.jar.*;
       
    32 import java.util.zip.*;
       
    33 import sun.misc.Launcher;
       
    34 
       
    35 /**
       
    36  * Handles the downloading of additional JRE components.  The bootstrap class
       
    37  * loader automatically invokes DownloadManager when it comes across a resource
       
    38  * that can't be located.
       
    39  *
       
    40  *@author Ethan Nicholas
       
    41  */
       
    42 public class DownloadManager {
       
    43     public static final String KERNEL_DOWNLOAD_URL_PROPERTY =
       
    44             "kernel.download.url";
       
    45     public static final String KERNEL_DOWNLOAD_ENABLED_PROPERTY =
       
    46             "kernel.download.enabled";
       
    47 
       
    48     public static final String KERNEL_DOWNLOAD_DIALOG_PROPERTY =
       
    49             "kernel.download.dialog";
       
    50 
       
    51     public static final String KERNEL_DEBUG_PROPERTY = "kernel.debug";
       
    52     // disables JRE completion when set to true, used as part of the build
       
    53     // process
       
    54     public static final String KERNEL_NOMERGE_PROPERTY = "kernel.nomerge";
       
    55 
       
    56     public static final String KERNEL_SIMULTANEOUS_DOWNLOADS_PROPERTY =
       
    57             "kernel.simultaneous.downloads";
       
    58 
       
    59     // used to bypass some problems with JAR entry modtimes not matching.
       
    60     // originally was set to zero, but apparently the epochs are different
       
    61     // for zip and pack so the pack/unpack cycle was causing the modtimes
       
    62     // to change.  With some recent changes to the reconstruction, I'm
       
    63     // not sure if this is actually necessary anymore.
       
    64     public static final int KERNEL_STATIC_MODTIME = 10000000;
       
    65 
       
    66     // indicates that bundles should be grabbed using getResource(), rather
       
    67     // than downloaded from a network path -- this is used during the build
       
    68     // process
       
    69     public static final String RESOURCE_URL = "internal-resource/";
       
    70     public static final String REQUESTED_BUNDLES_PATH = "lib" + File.separator +
       
    71             "bundles" + File.separator + "requested.list";
       
    72 
       
    73     private static final boolean disableDownloadDialog = "false".equals(
       
    74             System.getProperty(KERNEL_DOWNLOAD_DIALOG_PROPERTY));
       
    75 
       
    76     static boolean debug = "true".equals(
       
    77             System.getProperty(KERNEL_DEBUG_PROPERTY));
       
    78     // points to stderr in case we need to println before System.err is
       
    79     // initialized
       
    80     private static OutputStream errorStream;
       
    81     private static OutputStream logStream;
       
    82 
       
    83     static String MUTEX_PREFIX;
       
    84 
       
    85     static boolean complete;
       
    86 
       
    87     // 1 if jbroker started; 0 otherwise
       
    88     private static int _isJBrokerStarted = -1;
       
    89 
       
    90     // maps bundle names to URL strings
       
    91     private static Properties bundleURLs;
       
    92 
       
    93     public static final String JAVA_HOME = System.getProperty("java.home");
       
    94     public static final String USER_HOME = System.getProperty("user.home");
       
    95     public static final String JAVA_VERSION =
       
    96             System.getProperty("java.version");
       
    97     static final int BUFFER_SIZE = 2048;
       
    98 
       
    99     static volatile boolean jkernelLibLoaded = false;
       
   100 
       
   101     public static String DEFAULT_DOWNLOAD_URL =
       
   102         "http://javadl.sun.com/webapps/download/GetList/"
       
   103         +  System.getProperty("java.runtime.version") + "-kernel/windows-i586/";
       
   104 
       
   105     private static final String CUSTOM_PREFIX = "custom";
       
   106     private static final String KERNEL_PATH_SUFFIX = "-kernel";
       
   107 
       
   108     public static final String JAR_PATH_PROPERTY = "jarpath";
       
   109     public static final String SIZE_PROPERTY = "size";
       
   110     public static final String DEPENDENCIES_PROPERTY = "dependencies";
       
   111     public static final String INSTALL_PROPERTY = "install";
       
   112 
       
   113     private static boolean reportErrors = true;
       
   114 
       
   115     static final int ERROR_UNSPECIFIED = 0;
       
   116     static final int ERROR_DISK_FULL   = 1;
       
   117     static final int ERROR_MALFORMED_BUNDLE_PROPERTIES = 2;
       
   118     static final int ERROR_DOWNLOADING_BUNDLE_PROPERTIES = 3;
       
   119     static final int ERROR_MALFORMED_URL = 4;
       
   120     static final int ERROR_RETRY_CANCELLED = 5;
       
   121     static final int ERROR_NO_SUCH_BUNDLE = 6;
       
   122 
       
   123 
       
   124     // tracks whether the current thread is downloading.  A count of zero means
       
   125     // not currently downloading, >0 means the current thread is downloading or
       
   126     // installing a bundle.
       
   127     static ThreadLocal<Integer> downloading = new ThreadLocal<Integer>() {
       
   128         protected Integer initialValue() {
       
   129             return 0;
       
   130         }
       
   131     };
       
   132 
       
   133     private static File[] additionalBootStrapPaths = { };
       
   134 
       
   135     private static String[] bundleNames;
       
   136     private static String[] criticalBundleNames;
       
   137 
       
   138     private static String downloadURL;
       
   139 
       
   140     private static boolean visitorIdDetermined;
       
   141     private static String visitorId;
       
   142 
       
   143     /**
       
   144      * File and path where the Check value properties are gotten from
       
   145      */
       
   146     public static String CHECK_VALUES_FILE = "check_value.properties";
       
   147     static String CHECK_VALUES_DIR = "sun/jkernel/";
       
   148     static String CHECK_VALUES_PATH = CHECK_VALUES_DIR + CHECK_VALUES_FILE;
       
   149 
       
   150     /**
       
   151      * The contents of the bundle.properties file, which contains various
       
   152      * information about individual bundles.
       
   153      */
       
   154     private static Map<String, Map<String, String>> bundleProperties;
       
   155 
       
   156 
       
   157     /**
       
   158      * The contents of the resource_map file, which maps resources
       
   159      * to their respective bundles.
       
   160      */
       
   161     private static Map<String, String> resourceMap;
       
   162 
       
   163 
       
   164     /**
       
   165      * The contents of the file_map file, which maps files
       
   166      * to their respective bundles.
       
   167      */
       
   168     private static Map<String, String> fileMap;
       
   169 
       
   170     private static boolean extDirDetermined;
       
   171     private static boolean extDirIncluded;
       
   172 
       
   173     static {
       
   174         AccessController.doPrivileged(new PrivilegedAction() {
       
   175             public Object run() {
       
   176                 if (debug)
       
   177                     println("DownloadManager startup");
       
   178 
       
   179                  // this mutex is global and will apply to all different
       
   180                 // version of java kernel installed on the local machine
       
   181                 MUTEX_PREFIX = "jkernel";
       
   182                 boolean downloadEnabled = !"false".equals(
       
   183                         System.getProperty(KERNEL_DOWNLOAD_ENABLED_PROPERTY));
       
   184                 complete = !getBundlePath().exists() ||
       
   185                         !downloadEnabled;
       
   186 
       
   187                 // only load jkernel.dll if we are not "complete".
       
   188                 // DownloadManager will be loaded during build time, before
       
   189                 // jkernel.dll is built.  We only need to load jkernel.dll
       
   190                 // when DownloadManager needs to download something, which is
       
   191                 // not necessary during build time
       
   192                 if (!complete) {
       
   193                     loadJKernelLibrary();
       
   194                     log("Log opened");
       
   195 
       
   196                     if (isWindowsVista()) {
       
   197                         getLocalLowTempBundlePath().mkdirs();
       
   198                     }
       
   199 
       
   200                     new Thread() {
       
   201                         public void run() {
       
   202                             startBackgroundDownloads();
       
   203                         }
       
   204                     }.start();
       
   205 
       
   206                     try {
       
   207                         String dummyPath;
       
   208                         if (isWindowsVista()) {
       
   209                             dummyPath = USER_HOME +
       
   210                                     "\\appdata\\locallow\\dummy.kernel";
       
   211                         } else {
       
   212                             dummyPath = USER_HOME + "\\dummy.kernel";
       
   213                         }
       
   214 
       
   215                         File f = new File(dummyPath);
       
   216                         FileOutputStream out = new FileOutputStream(f, true);
       
   217                         out.close();
       
   218                         f.deleteOnExit();
       
   219 
       
   220                     } catch (IOException e) {
       
   221                         log(e);
       
   222                     }
       
   223                     // end of warm up code
       
   224 
       
   225                     new Thread("BundleDownloader") {
       
   226                         public void run() {
       
   227                             downloadRequestedBundles();
       
   228                         }
       
   229                     }.start();
       
   230                 }
       
   231                 return null;
       
   232             }
       
   233         });
       
   234     }
       
   235 
       
   236 
       
   237     static synchronized void loadJKernelLibrary() {
       
   238         if (!jkernelLibLoaded) {
       
   239             try {
       
   240                 System.loadLibrary("jkernel");
       
   241                 jkernelLibLoaded = true;
       
   242                 debug = getDebugProperty();
       
   243             } catch (Exception e) {
       
   244                 throw new Error(e);
       
   245             }
       
   246         }
       
   247     }
       
   248 
       
   249     static String appendTransactionId(String url) {
       
   250         StringBuilder result = new StringBuilder(url);
       
   251         String visitorId = DownloadManager.getVisitorId();
       
   252         if (visitorId != null) {
       
   253             if (url.indexOf("?") == -1)
       
   254                 result.append('?');
       
   255             else
       
   256                 result.append('&');
       
   257             result.append("transactionId=");
       
   258             result.append(DownloadManager.getVisitorId());
       
   259         }
       
   260         return result.toString();
       
   261     }
       
   262 
       
   263 
       
   264     /**
       
   265      * Returns the URL for the directory from which bundles should be
       
   266      * downloaded.
       
   267      */
       
   268     static synchronized String getBaseDownloadURL() {
       
   269         if (downloadURL == null) {
       
   270             log("Determining download URL...");
       
   271             loadJKernelLibrary();
       
   272 
       
   273             /*
       
   274              * First check if system property has been set - system
       
   275              * property should take over registry key setting.
       
   276              */
       
   277             downloadURL = System.getProperty(
       
   278                           DownloadManager.KERNEL_DOWNLOAD_URL_PROPERTY);
       
   279             log("System property kernel.download.url = " + downloadURL);
       
   280 
       
   281             /*
       
   282              * Now check if registry key has been set
       
   283              */
       
   284             if (downloadURL == null){
       
   285                 downloadURL = getUrlFromRegistry();
       
   286                 log("getUrlFromRegistry = " + downloadURL);
       
   287             }
       
   288 
       
   289             /*
       
   290              * Use default download url
       
   291              */
       
   292             if (downloadURL == null)
       
   293                 downloadURL = DEFAULT_DOWNLOAD_URL;
       
   294             log("Final download URL: " + downloadURL);
       
   295         }
       
   296         return downloadURL;
       
   297     }
       
   298 
       
   299 
       
   300     /**
       
   301      * Loads a file representing a node tree.  The format is described in
       
   302      * SplitJRE.writeTreeMap().  The node paths (such as
       
   303      * core/java/lang/Object.class) are interpreted with the root node as the
       
   304      * value and the remaining nodes as
       
   305      * the key, so the mapping for this entry would be java/lang/Object.class =
       
   306      * core.
       
   307      */
       
   308     static Map<String, String> readTreeMap(InputStream rawIn)
       
   309             throws IOException {
       
   310         // "token level" refers to the 0-31 byte that occurs prior to every
       
   311         // token in the stream, and would be e.g. <0> core <1> java <2> lang
       
   312         // <3> Object.class <3> String.class, which gives us two mappings:
       
   313         // java/lang/Object.class = core, and java/lang/String.class = core.
       
   314         // See the format description in SplitJRE.writeTreeMap for more details.
       
   315         Map<String, String> result = new HashMap<String, String>();
       
   316         InputStream in = new BufferedInputStream(rawIn);
       
   317         // holds the current token sequence,
       
   318         // e.g. {"core", "java", "lang", "Object.class"}
       
   319         List<String> tokens = new ArrayList<String>();
       
   320         StringBuilder currentToken = new StringBuilder();
       
   321         for (;;) {
       
   322             int c = in.read();
       
   323             if (c  == -1) // eof
       
   324                 break;
       
   325             if (c < 32) { // new token level
       
   326                 if (tokens.size() > 0) {
       
   327                     // replace the null at the end of the list with the token
       
   328                     // we just finished reading
       
   329                     tokens.set(tokens.size() - 1, currentToken.toString());
       
   330                 }
       
   331 
       
   332                 currentToken.setLength(0);
       
   333 
       
   334                 if (c > tokens.size()) {
       
   335                     // can't increase by more than one token level at a step
       
   336                     throw new InternalError("current token level is " +
       
   337                             (tokens.size() - 1) + " but encountered token " +
       
   338                             "level " + c);
       
   339                 }
       
   340                 else if (c == tokens.size()) {
       
   341                     // token level increased by 1; this means we are still
       
   342                     // adding tokens for the current mapping -- e.g. we have
       
   343                     // read "core", "java", "lang" and are just about to read
       
   344                     // "Object.class"
       
   345                     // add a placeholder for the new token
       
   346                     tokens.add(null);
       
   347                 }
       
   348                 else {
       
   349                     // we just stayed at the same level or backed up one or more
       
   350                     // token levels; this means that the current sequence is
       
   351                     // complete and needs to be added to the result map
       
   352                     StringBuilder key = new StringBuilder();
       
   353                     // combine all tokens except the first into a single string
       
   354                     for (int i = 1; i < tokens.size(); i++) {
       
   355                         if (i > 1)
       
   356                             key.append('/');
       
   357                         key.append(tokens.get(i));
       
   358                     }
       
   359                     // map the combined string to the first token, e.g.
       
   360                     // java/lang/Object.class = core
       
   361                     result.put(key.toString(), tokens.get(0));
       
   362                     // strip off tokens until we get back to the current token
       
   363                     // level
       
   364                     while (c < tokens.size())
       
   365                         tokens.remove(c);
       
   366                     // placeholder for upcoming token
       
   367                     tokens.add(null);
       
   368                 }
       
   369             }
       
   370             else if (c < 254) // character
       
   371                 currentToken.append((char) c);
       
   372             else if (c == 255)
       
   373                 currentToken.append(".class");
       
   374             else { // out-of-band value
       
   375                 throw new InternalError("internal error processing " +
       
   376                         "resource_map (can't-happen error)");
       
   377             }
       
   378         }
       
   379         if (tokens.size() > 0) // add token we just finished reading
       
   380             tokens.set(tokens.size() - 1, currentToken.toString());
       
   381         StringBuilder key = new StringBuilder();
       
   382         // add the last entry to the map
       
   383         for (int i = 1; i < tokens.size(); i++) {
       
   384             if (i > 1)
       
   385                 key.append('/');
       
   386             key.append(tokens.get(i));
       
   387         }
       
   388         if (!tokens.isEmpty())
       
   389             result.put(key.toString(), tokens.get(0));
       
   390         in.close();
       
   391         return Collections.unmodifiableMap(result);
       
   392     }
       
   393 
       
   394 
       
   395     /**
       
   396      * Returns the contents of the resource_map file, which maps
       
   397      * resources names to their respective bundles.
       
   398      */
       
   399     public static Map<String, String> getResourceMap() throws IOException {
       
   400         if (resourceMap == null) {
       
   401             InputStream in = DownloadManager.class.getResourceAsStream("resource_map");
       
   402             if (in != null) {
       
   403                 in = new BufferedInputStream(in);
       
   404                 try {
       
   405                     resourceMap = readTreeMap(in);
       
   406                     in.close();
       
   407                 }
       
   408                 catch (IOException e) {
       
   409                     // turns out we can be returned a broken stream instead of
       
   410                     // just null
       
   411                     resourceMap = new HashMap<String, String>();
       
   412                     complete = true;
       
   413                     log("Can't find resource_map, forcing complete to true");
       
   414                 }
       
   415                 in.close();
       
   416             }
       
   417             else {
       
   418                 resourceMap = new HashMap<String, String>();
       
   419                 complete = true;
       
   420                 log("Can't find resource_map, forcing complete to true");
       
   421             }
       
   422 
       
   423             for (int i = 1; ; i++) { // run through the numbered custom bundles
       
   424                 String name = CUSTOM_PREFIX + i;
       
   425                 File customPath = new File(getBundlePath(), name + ".jar");
       
   426                 if (customPath.exists()) {
       
   427                     JarFile custom = new JarFile(customPath);
       
   428                     Enumeration entries = custom.entries();
       
   429                     while (entries.hasMoreElements()) {
       
   430                         JarEntry entry = (JarEntry) entries.nextElement();
       
   431                         if (!entry.isDirectory())
       
   432                             resourceMap.put(entry.getName(), name);
       
   433                     }
       
   434                 }
       
   435                 else
       
   436                     break;
       
   437             }
       
   438         }
       
   439         return resourceMap;
       
   440     }
       
   441 
       
   442 
       
   443     /**
       
   444      * Returns the contents of the file_map file, which maps
       
   445      * file names to their respective bundles.
       
   446      */
       
   447     public static Map<String, String> getFileMap() throws IOException {
       
   448         if (fileMap == null) {
       
   449             InputStream in = DownloadManager.class.getResourceAsStream("file_map");
       
   450             if (in != null) {
       
   451                 in = new BufferedInputStream(in);
       
   452                 try {
       
   453                     fileMap = readTreeMap(in);
       
   454                     in.close();
       
   455                 }
       
   456                 catch (IOException e) {
       
   457                     // turns out we can be returned a broken stream instead of
       
   458                     // just null
       
   459                     fileMap = new HashMap<String, String>();
       
   460                     complete = true;
       
   461                     log("Can't find file_map, forcing complete to true");
       
   462                 }
       
   463                 in.close();
       
   464             }
       
   465             else {
       
   466                 fileMap = new HashMap<String, String>();
       
   467                 complete = true;
       
   468                 log("Can't find file_map, forcing complete to true");
       
   469             }
       
   470         }
       
   471         return fileMap;
       
   472     }
       
   473 
       
   474 
       
   475     /**
       
   476      * Returns the contents of the bundle.properties file, which maps
       
   477      * bundle names to a pipe-separated list of their properties.  Properties
       
   478      * include:
       
   479      * jarpath - By default, the JAR files (unpacked from classes.pack in the
       
   480      *           bundle) are stored under lib/bundles.  The jarpath property
       
   481      *           overrides this default setting, causing the JAR to be unpacked
       
   482      *           at the specified location.  This is used to preserve the
       
   483      *           identity of JRE JAR files such as lib/deploy.jar.
       
   484      * size    - The size of the download in bytes.
       
   485      */
       
   486     private static synchronized Map<String, Map<String, String>> getBundleProperties()
       
   487             throws IOException {
       
   488         if (bundleProperties == null) {
       
   489             InputStream in = DownloadManager.class.getResourceAsStream("bundle.properties");
       
   490             if (in == null) {
       
   491                 complete = true;
       
   492                 log("Can't find bundle.properties, forcing complete to true");
       
   493                 return null;
       
   494             }
       
   495             in = new BufferedInputStream(in);
       
   496             Properties tmp = new Properties();
       
   497             tmp.load(in);
       
   498             bundleProperties = new HashMap<String, Map<String, String>>();
       
   499             for (Map.Entry e : tmp.entrySet()) {
       
   500                 String key = (String) e.getKey();
       
   501                 String[] properties = ((String) e.getValue()).split("\\|");
       
   502                 Map<String, String> map = new HashMap<String, String>();
       
   503                 for (String entry : properties) {
       
   504                     int equals = entry.indexOf("=");
       
   505                     if (equals == -1)
       
   506                         throw new InternalError("error parsing bundle.properties: " +
       
   507                             entry);
       
   508                     map.put(entry.substring(0, equals).trim(),
       
   509                         entry.substring(equals + 1).trim());
       
   510                 }
       
   511                 bundleProperties.put(key, map);
       
   512             }
       
   513             in.close();
       
   514         }
       
   515         return bundleProperties;
       
   516     }
       
   517 
       
   518 
       
   519     /**
       
   520      * Returns a single bundle property value loaded from the bundle.properties
       
   521      * file.
       
   522      */
       
   523     static String getBundleProperty(String bundleName, String property) {
       
   524         try {
       
   525             Map<String, Map<String, String>> props = getBundleProperties();
       
   526             Map/*<String, String>*/ map = props != null ? props.get(bundleName) : null;
       
   527             return map != null ? (String) map.get(property) : null;
       
   528         }
       
   529         catch (IOException e) {
       
   530             throw new RuntimeException(e);
       
   531         }
       
   532     }
       
   533 
       
   534 
       
   535     /** Returns an array of all supported bundle names. */
       
   536     static String[] getBundleNames() throws IOException {
       
   537         if (bundleNames == null) {
       
   538             Set<String> result = new HashSet<String>();
       
   539             Map<String, String> resourceMap = getResourceMap();
       
   540             if (resourceMap != null)
       
   541                 result.addAll(resourceMap.values());
       
   542             Map<String, String> fileMap = getFileMap();
       
   543             if (fileMap != null)
       
   544                 result.addAll(fileMap.values());
       
   545             bundleNames = result.toArray(new String[result.size()]);
       
   546         }
       
   547         return bundleNames;
       
   548     }
       
   549 
       
   550 
       
   551     /**
       
   552      * Returns an array of all "critical" (must be downloaded prior to
       
   553      * completion) bundle names.
       
   554      */
       
   555     private static String[] getCriticalBundleNames() throws IOException {
       
   556         if (criticalBundleNames == null) {
       
   557             Set<String> result = new HashSet<String>();
       
   558             Map<String, String> fileMap = getFileMap();
       
   559             if (fileMap != null)
       
   560                 result.addAll(fileMap.values());
       
   561             criticalBundleNames = result.toArray(new String[result.size()]);
       
   562         }
       
   563         return criticalBundleNames;
       
   564     }
       
   565 
       
   566 
       
   567     public static void send(InputStream in, OutputStream out)
       
   568             throws IOException {
       
   569         byte[] buffer = new byte[BUFFER_SIZE];
       
   570         int c;
       
   571         while ((c = in.read(buffer)) > 0)
       
   572             out.write(buffer, 0, c);
       
   573     }
       
   574 
       
   575 
       
   576     /**
       
   577      * Determine whether all bundles have been downloaded, and if so create
       
   578      * the merged jars that will eventually replace rt.jar and resoures.jar.
       
   579      * IMPORTANT: this method should only be called from the background
       
   580      * download process.
       
   581      */
       
   582     static void performCompletionIfNeeded() {
       
   583         if (debug)
       
   584             log("DownloadManager.performCompletionIfNeeded: checking (" +
       
   585                     complete + ", " + System.getProperty(KERNEL_NOMERGE_PROPERTY)
       
   586                     + ")");
       
   587         if (complete ||
       
   588                 "true".equals(System.getProperty(KERNEL_NOMERGE_PROPERTY)))
       
   589             return;
       
   590         Bundle.loadReceipts();
       
   591         try {
       
   592             if (debug) {
       
   593                 List critical = new ArrayList(Arrays.asList(getCriticalBundleNames()));
       
   594                 critical.removeAll(Bundle.receipts);
       
   595                 log("DownloadManager.performCompletionIfNeeded: still need " +
       
   596                         critical.size() + " bundles (" + critical + ")");
       
   597             }
       
   598             if (Bundle.receipts.containsAll(Arrays.asList(getCriticalBundleNames()))) {
       
   599                 log("DownloadManager.performCompletionIfNeeded: running");
       
   600                 // all done!
       
   601                 new Thread("JarMerger") {
       
   602                     public void run() {
       
   603                         createMergedJars();
       
   604                     }
       
   605                 }.start();
       
   606             }
       
   607         }
       
   608         catch (IOException e) {
       
   609             throw new RuntimeException(e);
       
   610         }
       
   611     }
       
   612 
       
   613 
       
   614     /**
       
   615      * Returns the bundle corresponding to a given resource path (e.g.
       
   616      * "java/lang/Object.class").  If the resource does not appear in a bundle,
       
   617      * null is returned.
       
   618      */
       
   619     public static Bundle getBundleForResource(String resource)
       
   620             throws IOException {
       
   621         String bundleName = getResourceMap().get(resource);
       
   622         return bundleName != null ? Bundle.getBundle(bundleName) : null;
       
   623     }
       
   624 
       
   625 
       
   626     /**
       
   627      * Returns the bundle corresponding to a given JRE file path (e.g.
       
   628      * "bin/awt.dll").  If the file does not appear in a bundle, null is
       
   629      * returned.
       
   630      */
       
   631     private static Bundle getBundleForFile(String file) throws IOException {
       
   632         String bundleName = getFileMap().get(file);
       
   633         return bundleName != null ? Bundle.getBundle(bundleName) : null;
       
   634     }
       
   635 
       
   636 
       
   637     /**
       
   638      * Returns the path to the lib/bundles directory.
       
   639      */
       
   640     static File getBundlePath() {
       
   641         return new File(JAVA_HOME, "lib" + File.separatorChar + "bundles");
       
   642     }
       
   643 
       
   644     private static String getAppDataLocalLow() {
       
   645         return USER_HOME + "\\appdata\\locallow\\";
       
   646     }
       
   647 
       
   648     public static String getKernelJREDir() {
       
   649         return "kerneljre" + JAVA_VERSION;
       
   650     }
       
   651 
       
   652     static File getLocalLowTempBundlePath() {
       
   653         return new File(getLocalLowKernelJava() + "-bundles");
       
   654     }
       
   655 
       
   656     static String getLocalLowKernelJava() {
       
   657         return getAppDataLocalLow() + getKernelJREDir();
       
   658     }
       
   659 
       
   660     /**
       
   661      * Returns an array of JAR files which have been added to the boot strap
       
   662      * class path since the JVM was first booted.
       
   663      */
       
   664     public static synchronized File[] getAdditionalBootStrapPaths() {
       
   665         return additionalBootStrapPaths != null ? additionalBootStrapPaths :
       
   666                 new File[0];
       
   667     }
       
   668 
       
   669 
       
   670     private static void addEntryToBootClassPath(File path) {
       
   671         // Must acquire these locks in this order
       
   672         synchronized(Launcher.class) {
       
   673            synchronized(DownloadManager.class) {
       
   674                 File[] newBootStrapPaths = new File[
       
   675                     additionalBootStrapPaths.length + 1];
       
   676                 System.arraycopy(additionalBootStrapPaths, 0, newBootStrapPaths,
       
   677                         0, additionalBootStrapPaths.length);
       
   678                 newBootStrapPaths[newBootStrapPaths.length - 1] = path;
       
   679                 additionalBootStrapPaths = newBootStrapPaths;
       
   680                 Launcher.flushBootstrapClassPath();
       
   681            }
       
   682        }
       
   683     }
       
   684 
       
   685 
       
   686     /**
       
   687      * Scan through java.ext.dirs to see if the lib/ext directory is included.
       
   688      * If not, we shouldn't be "finding" lib/ext jars for download.
       
   689      */
       
   690     private static synchronized boolean extDirIsIncluded() {
       
   691         if (!extDirDetermined) {
       
   692             extDirDetermined = true;
       
   693             String raw = System.getProperty("java.ext.dirs");
       
   694             String ext = JAVA_HOME + File.separator + "lib" + File.separator + "ext";
       
   695             int index = 0;
       
   696             while (index < raw.length()) {
       
   697                 int newIndex = raw.indexOf(File.pathSeparator, index);
       
   698                 if (newIndex == -1)
       
   699                     newIndex = raw.length();
       
   700                 String path = raw.substring(index, newIndex);
       
   701                 if (path.equals(ext)) {
       
   702                     extDirIncluded = true;
       
   703                     break;
       
   704                 }
       
   705                 index = newIndex + 1;
       
   706             }
       
   707         }
       
   708         return extDirIncluded;
       
   709     }
       
   710 
       
   711 
       
   712     private static String doGetBootClassPathEntryForResource(
       
   713             String resourceName) {
       
   714         boolean retry = false;
       
   715         do {
       
   716             Bundle bundle = null;
       
   717             try {
       
   718                 bundle = getBundleForResource(resourceName);
       
   719                 if (bundle != null) {
       
   720                     File path = bundle.getJarPath();
       
   721                     boolean isExt = path.getParentFile().getName().equals("ext");
       
   722                     if (isExt && !extDirIsIncluded()) // this is a lib/ext jar, but
       
   723                         return null;                  // lib/ext isn't in the path
       
   724                     if (getBundleProperty(bundle.getName(), JAR_PATH_PROPERTY) == null) {
       
   725                         // if the bundle doesn't have its own JAR path, that means it's
       
   726                         // going to be merged into rt.jar.  If we already have the
       
   727                         // merged rt.jar, we can simply point to that.
       
   728                         Bundle merged = Bundle.getBundle("merged");
       
   729                         if (merged != null && merged.isInstalled()) {
       
   730                             File jar;
       
   731                             if (resourceName.endsWith(".class"))
       
   732                                 jar = merged.getJarPath();
       
   733                             else
       
   734                                 jar = new File(merged.getJarPath().getPath().replaceAll("merged-rt.jar",
       
   735                                         "merged-resources.jar"));
       
   736                             addEntryToBootClassPath(jar);
       
   737                             return jar.getPath();
       
   738                         }
       
   739                     }
       
   740                     if (!bundle.isInstalled()) {
       
   741                         bundle.queueDependencies(true);
       
   742                         log("On-demand downloading " +
       
   743                                 bundle.getName() + " for resource " +
       
   744                                 resourceName + "...");
       
   745                         bundle.install();
       
   746                         log(bundle + " install finished.");
       
   747                     }
       
   748                     log("Double-checking " + bundle + " state...");
       
   749                     if (!bundle.isInstalled()) {
       
   750                         throw new IllegalStateException("Expected state of " +
       
   751                                 bundle + " to be INSTALLED");
       
   752                     }
       
   753                     if (isExt) {
       
   754                         // don't add lib/ext entries to the boot class path, add
       
   755                         // them to the extension classloader instead
       
   756                         Launcher.addURLToExtClassLoader(path.toURL());
       
   757                         return null;
       
   758                     }
       
   759 
       
   760                     if ("javaws".equals(bundle.getName())) {
       
   761                         Launcher.addURLToAppClassLoader(path.toURL());
       
   762                         log("Returning null for javaws");
       
   763                         return null;
       
   764                     }
       
   765 
       
   766                     if ("core".equals(bundle.getName()))
       
   767                         return null;
       
   768 
       
   769                     // else add to boot class path
       
   770                     addEntryToBootClassPath(path);
       
   771 
       
   772                     return path.getPath();
       
   773                 }
       
   774                 return null; // not one of the JRE's classes
       
   775             }
       
   776             catch (Throwable e) {
       
   777                 retry = handleException(e);
       
   778                 log("Error downloading bundle for " +
       
   779                         resourceName + ":");
       
   780                 log(e);
       
   781                 if (e instanceof IOException) {
       
   782                     // bundle did not get installed correctly, remove incomplete
       
   783                     // bundle files
       
   784                     if (bundle != null) {
       
   785                         if (bundle.getJarPath() != null) {
       
   786                             File packTmp = new File(bundle.getJarPath() + ".pack");
       
   787                             packTmp.delete();
       
   788                             bundle.getJarPath().delete();
       
   789                         }
       
   790                         if (bundle.getLocalPath() != null) {
       
   791                             bundle.getLocalPath().delete();
       
   792                         }
       
   793                         bundle.setState(Bundle.NOT_DOWNLOADED);
       
   794                     }
       
   795                 }
       
   796             }
       
   797         } while (retry);
       
   798         sendErrorPing(ERROR_RETRY_CANCELLED); // bundle failed to install, user cancelled
       
   799 
       
   800         return null; // failed, user chose not to retry
       
   801     }
       
   802 
       
   803     static synchronized void sendErrorPing(int code) {
       
   804         try {
       
   805             File bundlePath;
       
   806             if (isWindowsVista()) {
       
   807                 bundlePath = getLocalLowTempBundlePath();
       
   808             } else {
       
   809                 bundlePath = getBundlePath();
       
   810             }
       
   811             File tmp = new File(bundlePath, "tmp");
       
   812             File errors = new File(tmp, "errors");
       
   813             String errorString = String.valueOf(code);
       
   814             if (errors.exists()) {
       
   815                 BufferedReader in = new BufferedReader(new FileReader(errors));
       
   816                 String line = in.readLine();
       
   817                 while (line != null) {
       
   818                     if (line.equals(errorString))
       
   819                         return; // we have already pinged this error
       
   820                     line = in.readLine();
       
   821                 }
       
   822             }
       
   823             tmp.mkdirs();
       
   824             Writer out = new FileWriter(errors, true);
       
   825             out.write(errorString + System.getProperty("line.separator"));
       
   826             out.close();
       
   827             postDownloadError(code);
       
   828         }
       
   829         catch (IOException e) {
       
   830             e.printStackTrace();
       
   831         }
       
   832     }
       
   833 
       
   834 
       
   835 
       
   836     /**
       
   837      * Displays an error dialog and prompts the user to retry or cancel.
       
   838      * Returns true if the user chose to retry, false if he chose to cancel.
       
   839      */
       
   840     static boolean handleException(Throwable e) {
       
   841         if (e instanceof IOException) {
       
   842             // I don't know of a better method to determine the root cause of
       
   843             // the exception, unfortunately...
       
   844             int code = ERROR_UNSPECIFIED;
       
   845             if (e.getMessage().indexOf("not enough space") != -1)
       
   846                 code = ERROR_DISK_FULL;
       
   847             return askUserToRetryDownloadOrQuit(code);
       
   848         }
       
   849         else
       
   850             return false;
       
   851     }
       
   852 
       
   853 
       
   854     static synchronized void flushBundleURLs() {
       
   855         bundleURLs = null;
       
   856     }
       
   857 
       
   858 
       
   859     static synchronized Properties getBundleURLs(boolean showUI)
       
   860             throws IOException {
       
   861         if (bundleURLs == null) {
       
   862             log("Entering DownloadManager.getBundleURLs");
       
   863             String base = getBaseDownloadURL();
       
   864             String url = appendTransactionId(base);
       
   865             // use PID instead of createTempFile or other random filename so as
       
   866             // to avoid dependencies on the random number generator libraries
       
   867             File bundlePath = null;
       
   868             // write temp file to locallow directory on vista
       
   869             if (isWindowsVista()) {
       
   870                 bundlePath = getLocalLowTempBundlePath();
       
   871             } else {
       
   872                 bundlePath = getBundlePath();
       
   873             }
       
   874             File tmp = new File(bundlePath, "urls." + getCurrentProcessId() +
       
   875                     ".properties");
       
   876             try {
       
   877                 log("Downloading from " + url + " to " + tmp);
       
   878                 downloadFromURL(url, tmp, "", showUI);
       
   879                 bundleURLs = new Properties();
       
   880                 if (tmp.exists()) {
       
   881                     addToTotalDownloadSize((int) tmp.length()); // better late than never
       
   882                     InputStream in = new FileInputStream(tmp);
       
   883                     in = new BufferedInputStream(in);
       
   884                     bundleURLs.load(in);
       
   885                     in.close();
       
   886                     if (bundleURLs.isEmpty()) {
       
   887                         fatalError(ERROR_MALFORMED_BUNDLE_PROPERTIES);
       
   888                     }
       
   889                 } else {
       
   890                     fatalError(ERROR_DOWNLOADING_BUNDLE_PROPERTIES);
       
   891                 }
       
   892             } finally {
       
   893                 // delete the temp file
       
   894                 if (!debug)
       
   895                     tmp.delete();
       
   896             }
       
   897             log("Leaving DownloadManager.getBundleURLs");
       
   898             // else an error occurred and user chose not to retry; leave
       
   899             // bundleURLs empty so we don't continually try to re-download it
       
   900         }
       
   901         return bundleURLs;
       
   902     }
       
   903 
       
   904     /**
       
   905      * Checks to see if the specified resource is part of a bundle, and if so
       
   906      * downloads it.  Returns either a string which should be added to the boot
       
   907      * class path (the newly-downloaded JAR's location), or null to indicate
       
   908      * that it isn't one of the JRE's resources or could not be downloaded.
       
   909      */
       
   910     public static String getBootClassPathEntryForResource(
       
   911             final String resourceName) {
       
   912         if (debug)
       
   913             log("Entering getBootClassPathEntryForResource(" + resourceName + ")");
       
   914         if (isJREComplete() || downloading == null ||
       
   915                 resourceName.startsWith("sun/jkernel")) {
       
   916             if (debug)
       
   917                 log("Bailing: " + isJREComplete() + ", " + (downloading == null));
       
   918             return null;
       
   919         }
       
   920         incrementDownloadCount();
       
   921         try {
       
   922             String result = (String) AccessController.doPrivileged(
       
   923                 new PrivilegedAction() {
       
   924                     public Object run() {
       
   925                         return (String) doGetBootClassPathEntryForResource(
       
   926                                 resourceName);
       
   927                     }
       
   928                 }
       
   929             );
       
   930             log("getBootClassPathEntryForResource(" + resourceName + ") == " + result);
       
   931             return result;
       
   932         }
       
   933         finally {
       
   934             decrementDownloadCount();
       
   935         }
       
   936     }
       
   937 
       
   938 
       
   939     /**
       
   940      * Called by the boot class loader when it encounters a class it can't find.
       
   941      * This method will check to see if the class is part of a bundle, and if so
       
   942      * download it.  Returns either a string which should be added to the boot
       
   943      * class path (the newly-downloaded JAR's location), or null to indicate
       
   944      * that it isn't one of the JRE's classes or could not be downloaded.
       
   945      */
       
   946     public static String getBootClassPathEntryForClass(final String className) {
       
   947         return getBootClassPathEntryForResource(className.replace('.', '/') +
       
   948                 ".class");
       
   949     }
       
   950 
       
   951 
       
   952     private static boolean doDownloadFile(String relativePath)
       
   953             throws IOException {
       
   954         Bundle bundle = getBundleForFile(relativePath);
       
   955         if (bundle != null) {
       
   956             bundle.queueDependencies(true);
       
   957             log("On-demand downloading " + bundle.getName() +
       
   958                     " for file " + relativePath + "...");
       
   959             bundle.install();
       
   960             return true;
       
   961         }
       
   962         return false;
       
   963     }
       
   964 
       
   965 
       
   966     /**
       
   967      * Locates the bundle for the specified JRE file (e.g. "bin/awt.dll") and
       
   968      * installs it.  Returns true if the file is indeed part of the JRE and has
       
   969      * now been installed, false if the file is not part of the JRE, and throws
       
   970      * an IOException if the file is part of the JRE but could not be
       
   971      * downloaded.
       
   972      */
       
   973     public static boolean downloadFile(final String relativePath)
       
   974             throws IOException {
       
   975         if (isJREComplete() || downloading == null)
       
   976             return false;
       
   977 
       
   978         incrementDownloadCount();
       
   979         try {
       
   980             Object result =
       
   981                     AccessController.doPrivileged(new PrivilegedAction() {
       
   982                 public Object run() {
       
   983                     File path = new File(JAVA_HOME,
       
   984                             relativePath.replace('/', File.separatorChar));
       
   985                     if (path.exists())
       
   986                         return true;
       
   987                     try {
       
   988                         return new Boolean(doDownloadFile(relativePath));
       
   989                     }
       
   990                     catch (IOException e) {
       
   991                         return e;
       
   992                     }
       
   993                 }
       
   994             });
       
   995             if (result instanceof Boolean)
       
   996                 return ((Boolean) result).booleanValue();
       
   997             else
       
   998                 throw (IOException) result;
       
   999         }
       
  1000         finally {
       
  1001             decrementDownloadCount();
       
  1002         }
       
  1003     }
       
  1004 
       
  1005 
       
  1006     // increments the counter that tracks whether the current thread is involved
       
  1007     // in any download-related activities.  A non-zero count indicates that the
       
  1008     // thread is currently downloading or installing a bundle.
       
  1009     static void incrementDownloadCount() {
       
  1010         downloading.set(downloading.get() + 1);
       
  1011     }
       
  1012 
       
  1013 
       
  1014     // increments the counter that tracks whether the current thread is involved
       
  1015     // in any download-related activities.  A non-zero count indicates that the
       
  1016     // thread is currently downloading or installing a bundle.
       
  1017     static void decrementDownloadCount() {
       
  1018         // will generate an exception if incrementDownloadCount() hasn't been
       
  1019         // called first, this is intentional
       
  1020         downloading.set(downloading.get() - 1);
       
  1021     }
       
  1022 
       
  1023 
       
  1024     /**
       
  1025      * Returns <code>true</code> if the current thread is in the process of
       
  1026      * downloading a bundle.  This is called by ClassLoader.loadLibrary(), so
       
  1027      * that when we run into a library required by the download process itself,
       
  1028      * we don't call back into DownloadManager in an attempt to download it
       
  1029      * (which would lead to infinite recursion).
       
  1030      *
       
  1031      * All classes and libraries required to download classes must by
       
  1032      * definition already be present.  So if this method returns true, we are
       
  1033      * currently in the middle of performing a download, and the class or
       
  1034      * library load must be happening due to the download itself.  We can
       
  1035      * immediately abort such requests -- the class or library should already
       
  1036      * be present.  If it isn't, we're not going to be able to download it,
       
  1037      * since we have just established that it is required to perform a
       
  1038      * download, and we might as well just let the NoClassDefFoundError /
       
  1039      * UnsatisfiedLinkError occur.
       
  1040      */
       
  1041     public static boolean isCurrentThreadDownloading() {
       
  1042         return downloading != null ? downloading.get() > 0 : false;
       
  1043     }
       
  1044 
       
  1045 
       
  1046     /**
       
  1047      * Returns true if everything is downloaded and the JRE has been
       
  1048      * reconstructed.  Also returns true if kernel functionality is disabled
       
  1049      * for any other reason.
       
  1050      */
       
  1051     public static boolean isJREComplete() {
       
  1052         return complete;
       
  1053     }
       
  1054 
       
  1055 
       
  1056     // called by BackgroundDownloader
       
  1057     static void doBackgroundDownloads(boolean showProgress) {
       
  1058         if (!complete) {
       
  1059             if (!showProgress && !debug)
       
  1060                 reportErrors = false;
       
  1061             try {
       
  1062                 // install swing first for ergonomic reasons
       
  1063                 Bundle swing = Bundle.getBundle("javax_swing_core");
       
  1064                 if (!swing.isInstalled())
       
  1065                     swing.install(showProgress, false, false);
       
  1066                 // install remaining bundles
       
  1067                 for (String name : getCriticalBundleNames()) {
       
  1068                     Bundle bundle = Bundle.getBundle(name);
       
  1069                     if (!bundle.isInstalled()) {
       
  1070                         bundle.install(showProgress, false, true);
       
  1071                     }
       
  1072                 }
       
  1073                 shutdown();
       
  1074             }
       
  1075             catch (IOException e) {
       
  1076                 log(e);
       
  1077             }
       
  1078         }
       
  1079     }
       
  1080 
       
  1081     // copy receipt file to destination path specified
       
  1082     static void copyReceiptFile(File from, File to) throws IOException {
       
  1083         DataInputStream in = new DataInputStream(
       
  1084                 new BufferedInputStream(new FileInputStream(from)));
       
  1085         OutputStream out = new FileOutputStream(to);
       
  1086         String line = in.readLine();
       
  1087         while (line != null) {
       
  1088             out.write((line + '\n').getBytes("utf-8"));
       
  1089             line = in.readLine();
       
  1090         }
       
  1091         in.close();
       
  1092         out.close();
       
  1093     }
       
  1094 
       
  1095 
       
  1096     private static void downloadRequestedBundles() {
       
  1097         log("Checking for requested bundles...");
       
  1098         try {
       
  1099             File list = new File(JAVA_HOME, REQUESTED_BUNDLES_PATH);
       
  1100             if (list.exists()) {
       
  1101                 FileInputStream in = new FileInputStream(list);
       
  1102                 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
       
  1103                 send(in, buffer);
       
  1104                 in.close();
       
  1105 
       
  1106                 // split string manually to avoid relying on regexes or
       
  1107                 // StringTokenizer
       
  1108                 String raw = new String(buffer.toByteArray(), "utf-8");
       
  1109                 List/*<String>*/ bundles = new ArrayList/*<String>*/();
       
  1110                 StringBuilder token = new StringBuilder();
       
  1111                 for (int i = 0; i < raw.length(); i++) {
       
  1112                     char c = raw.charAt(i);
       
  1113                     if (c == ',' || Character.isWhitespace(c)) {
       
  1114                         if (token.length() > 0) {
       
  1115                             bundles.add(token.toString());
       
  1116                             token.setLength(0);
       
  1117                         }
       
  1118                     }
       
  1119                     else
       
  1120                         token.append(c);
       
  1121                 }
       
  1122                 if (token.length() > 0)
       
  1123                     bundles.add(token.toString());
       
  1124                 log("Requested bundles: " + bundles);
       
  1125                 for (int i = 0; i < bundles.size(); i++) {
       
  1126                     Bundle bundle = Bundle.getBundle((String) bundles.get(i));
       
  1127                     if (bundle != null && !bundle.isInstalled()) {
       
  1128                         log("Downloading " + bundle + " due to requested.list");
       
  1129                         bundle.install(true, false, false);
       
  1130                     }
       
  1131                 }
       
  1132             }
       
  1133         }
       
  1134         catch (IOException e) {
       
  1135             log(e);
       
  1136         }
       
  1137     }
       
  1138 
       
  1139 
       
  1140     static void fatalError(int code) {
       
  1141         fatalError(code, null);
       
  1142     }
       
  1143 
       
  1144 
       
  1145     /**
       
  1146      * Called to cleanly shut down the VM when a fatal download error has
       
  1147      * occurred.  Calls System.exit() if outside of the Java Plug-In, otherwise
       
  1148      * throws an error.
       
  1149      */
       
  1150     static void fatalError(int code, String arg) {
       
  1151         sendErrorPing(code);
       
  1152 
       
  1153         for (int i = 0; i < Bundle.THREADS; i++)
       
  1154             bundleInstallComplete();
       
  1155         if (reportErrors)
       
  1156             displayError(code, arg);
       
  1157         // inPlugIn check isn't 100% reliable but should be close enough.
       
  1158         // headless is for the browser side of things in the out-of-process
       
  1159         // plug-in
       
  1160         boolean inPlugIn = (Boolean.getBoolean("java.awt.headless") ||
       
  1161            System.getProperty("javaplugin.version") != null);
       
  1162         KernelError error = new KernelError("Java Kernel bundle download failed");
       
  1163         if (inPlugIn)
       
  1164             throw error;
       
  1165         else {
       
  1166             log(error);
       
  1167             System.exit(1);
       
  1168         }
       
  1169     }
       
  1170 
       
  1171 
       
  1172     // start the background download process using the jbroker broker process
       
  1173     // the method will first launch the broker process, if it is not already
       
  1174     // running
       
  1175     // it will then send the command necessary to start the background download
       
  1176     // process to the broker process
       
  1177     private static void startBackgroundDownloadWithBroker() {
       
  1178 
       
  1179         if (!BackgroundDownloader.getBackgroundDownloadProperty()) {
       
  1180             // If getBackgroundDownloadProperty() returns false
       
  1181             // we're doing the downloads from this VM; we don't want to
       
  1182             // spawn another one
       
  1183             return;
       
  1184         }
       
  1185 
       
  1186         // launch broker process if necessary
       
  1187         if (!launchBrokerProcess()) {
       
  1188             return;
       
  1189         }
       
  1190 
       
  1191 
       
  1192         String kernelDownloadURLProperty = getBaseDownloadURL();
       
  1193 
       
  1194         String kernelDownloadURL;
       
  1195 
       
  1196         // only set KERNEL_DOWNLOAD_URL_PROPERTY if we override
       
  1197         // the default download url
       
  1198         if (kernelDownloadURLProperty == null ||
       
  1199                 kernelDownloadURLProperty.equals(DEFAULT_DOWNLOAD_URL)) {
       
  1200             kernelDownloadURL = " ";
       
  1201         } else {
       
  1202             kernelDownloadURL = kernelDownloadURLProperty;
       
  1203         }
       
  1204 
       
  1205         startBackgroundDownloadWithBrokerImpl(kernelDownloadURLProperty);
       
  1206     }
       
  1207 
       
  1208     private static void startBackgroundDownloads() {
       
  1209         if (!complete) {
       
  1210             if (BackgroundDownloader.getBackgroundMutex().acquire(0)) {
       
  1211                 // we don't actually need to hold the mutex -- it was just a
       
  1212                 // quick check to see if there is any point in even attempting
       
  1213                 // to start the background downloader
       
  1214                 BackgroundDownloader.getBackgroundMutex().release();
       
  1215                 if (isWindowsVista()) {
       
  1216                     // use broker process to start background download
       
  1217                     // at high integrity
       
  1218                     startBackgroundDownloadWithBroker();
       
  1219                 } else {
       
  1220                     BackgroundDownloader.startBackgroundDownloads();
       
  1221                 }
       
  1222             }
       
  1223         }
       
  1224     }
       
  1225 
       
  1226 
       
  1227     /**
       
  1228      * Increases the total download size displayed in the download progress
       
  1229      * dialog.
       
  1230      */
       
  1231     static native void addToTotalDownloadSize(int size);
       
  1232 
       
  1233 
       
  1234     /**
       
  1235      * Displays a progress dialog while downloading from the specified URL.
       
  1236      *
       
  1237      *@param url the URL string from which to download
       
  1238      *@param file the destination path
       
  1239      *@param name the user-visible name of the component we are downloading
       
  1240      */
       
  1241     static void downloadFromURL(String url, File file, String name,
       
  1242             boolean showProgress) {
       
  1243         // do not show download dialog if kernel.download.dialog is false
       
  1244         downloadFromURLImpl(url, file, name,
       
  1245                 disableDownloadDialog ? false : showProgress);
       
  1246     }
       
  1247 
       
  1248     private static native void downloadFromURLImpl(String url, File file,
       
  1249             String name, boolean showProgress);
       
  1250 
       
  1251     // This is for testing purposes only - allows to specify URL
       
  1252     // to download kernel bundles from through the registry key.
       
  1253     static native String getUrlFromRegistry();
       
  1254 
       
  1255     static native String getVisitorId0();
       
  1256 
       
  1257     static native void postDownloadComplete();
       
  1258 
       
  1259     static native void postDownloadError(int code);
       
  1260 
       
  1261     // Returns the visitor ID set by the installer, will be sent to the server
       
  1262     // during bundle downloads for logging purposes.
       
  1263     static synchronized String getVisitorId() {
       
  1264         if (!visitorIdDetermined) {
       
  1265             visitorIdDetermined = true;
       
  1266             visitorId = getVisitorId0();
       
  1267         }
       
  1268         return visitorId;
       
  1269     }
       
  1270 
       
  1271     // display an error message using a native dialog
       
  1272     public static native void displayError(int code, String arg);
       
  1273 
       
  1274     // prompt user whether to retry download, or quit
       
  1275     // returns true if the user chose to retry
       
  1276     public static native boolean askUserToRetryDownloadOrQuit(int code);
       
  1277 
       
  1278     // returns true if we are running Windows Vista; false otherwise
       
  1279     static native boolean isWindowsVista();
       
  1280 
       
  1281     private static native void startBackgroundDownloadWithBrokerImpl(
       
  1282             String command);
       
  1283 
       
  1284     private static int isJBrokerStarted() {
       
  1285         if (_isJBrokerStarted == -1) {
       
  1286             // initialize state of jbroker
       
  1287             _isJBrokerStarted = isJBrokerRunning() ? 1 : 0;
       
  1288         }
       
  1289         return _isJBrokerStarted;
       
  1290     }
       
  1291 
       
  1292     // returns true if broker process (jbroker) is running; false otherwise
       
  1293     private static native boolean isJBrokerRunning();
       
  1294 
       
  1295     // returns true if we are running in IE protected mode; false otherwise
       
  1296     private static native boolean isIEProtectedMode();
       
  1297 
       
  1298     private static native boolean launchJBroker(String jbrokerPath);
       
  1299 
       
  1300     static native void bundleInstallStart();
       
  1301 
       
  1302     static native void bundleInstallComplete();
       
  1303 
       
  1304     private static native boolean moveFileWithBrokerImpl(String fromPath,
       
  1305             String userHome);
       
  1306 
       
  1307     private static native boolean moveDirWithBrokerImpl(String fromPath,
       
  1308             String userHome);
       
  1309 
       
  1310     static boolean moveFileWithBroker(String fromPath) {
       
  1311         // launch jbroker if necessary
       
  1312         if (!launchBrokerProcess()) {
       
  1313             return false;
       
  1314         }
       
  1315 
       
  1316         return moveFileWithBrokerImpl(fromPath, USER_HOME);
       
  1317     }
       
  1318 
       
  1319     static boolean moveDirWithBroker(String fromPath) {
       
  1320         // launch jbroker if necessary
       
  1321         if (!launchBrokerProcess()) {
       
  1322             return false;
       
  1323         }
       
  1324 
       
  1325         return moveDirWithBrokerImpl(fromPath, USER_HOME);
       
  1326     }
       
  1327 
       
  1328     private static synchronized boolean launchBrokerProcess() {
       
  1329         // launch jbroker if necessary
       
  1330         if (isJBrokerStarted() == 0) {
       
  1331             // launch jbroker if needed
       
  1332             boolean ret = launchJBroker(JAVA_HOME);
       
  1333             // set state of jbroker
       
  1334             _isJBrokerStarted = ret ? 1 : 0;
       
  1335             return ret;
       
  1336         }
       
  1337         return true;
       
  1338     }
       
  1339 
       
  1340     private static class StreamMonitor implements Runnable {
       
  1341         private InputStream istream;
       
  1342         public StreamMonitor(InputStream stream) {
       
  1343             istream = new BufferedInputStream(stream);
       
  1344             new Thread(this).start();
       
  1345         }
       
  1346         public void run() {
       
  1347             byte[] buffer = new byte[4096];
       
  1348             try {
       
  1349                 int ret = istream.read(buffer);
       
  1350                 while (ret != -1) {
       
  1351                     ret = istream.read(buffer);
       
  1352                 }
       
  1353             } catch (IOException e) {
       
  1354                 try {
       
  1355                     istream.close();
       
  1356                 } catch (IOException e2) {
       
  1357                 } // Should allow clean exit when process shuts down
       
  1358             }
       
  1359         }
       
  1360     }
       
  1361 
       
  1362 
       
  1363     /** Copy a file tree, excluding certain named files. */
       
  1364     private static void copyAll(File src, File dest, Set/*<String>*/ excludes)
       
  1365                             throws IOException {
       
  1366         if (!excludes.contains(src.getName())) {
       
  1367             if (src.isDirectory()) {
       
  1368                 File[] children = src.listFiles();
       
  1369                 if (children != null) {
       
  1370                     for (int i = 0; i < children.length; i++)
       
  1371                         copyAll(children[i],
       
  1372                                 new File(dest, children[i].getName()),
       
  1373                                 excludes);
       
  1374                 }
       
  1375             }
       
  1376             else {
       
  1377                 dest.getParentFile().mkdirs();
       
  1378                 FileInputStream in = new FileInputStream(src);
       
  1379                 FileOutputStream out = new FileOutputStream(dest);
       
  1380                 send(in, out);
       
  1381                 in.close();
       
  1382                 out.close();
       
  1383             }
       
  1384         }
       
  1385     }
       
  1386 
       
  1387 
       
  1388     public static void dumpOutput(final Process p) {
       
  1389         Thread outputReader = new Thread("outputReader") {
       
  1390             public void run() {
       
  1391                 try {
       
  1392                     InputStream in = p.getInputStream();
       
  1393                     DownloadManager.send(in, System.out);
       
  1394                 } catch (IOException e) {
       
  1395                     log(e);
       
  1396                 }
       
  1397             }
       
  1398         };
       
  1399         outputReader.start();
       
  1400         Thread errorReader = new Thread("errorReader") {
       
  1401             public void run() {
       
  1402                 try {
       
  1403                     InputStream in = p.getErrorStream();
       
  1404                     DownloadManager.send(in, System.err);
       
  1405                 } catch (IOException e) {
       
  1406                     log(e);
       
  1407                 }
       
  1408             }
       
  1409         };
       
  1410         errorReader.start();
       
  1411     }
       
  1412 
       
  1413 
       
  1414     /**
       
  1415      * Creates the merged rt.jar and resources.jar files.
       
  1416      */
       
  1417     private static void createMergedJars() {
       
  1418         log("DownloadManager.createMergedJars");
       
  1419         File bundlePath;
       
  1420         if (isWindowsVista()) {
       
  1421             bundlePath = getLocalLowTempBundlePath();
       
  1422         } else {
       
  1423             bundlePath = getBundlePath();
       
  1424         }
       
  1425         File tmp = new File(bundlePath, "tmp");
       
  1426         // explicitly check the final location, not the (potentially) local-low
       
  1427         // location -- a local-low finished isn't good enough to call it done
       
  1428         if (new File(getBundlePath(), "tmp" + File.separator + "finished").exists())
       
  1429             return; // already done
       
  1430         log("DownloadManager.createMergedJars: running");
       
  1431         tmp.mkdirs();
       
  1432         boolean retry = false;
       
  1433         do {
       
  1434             try {
       
  1435                 Bundle.getBundle("merged").install(false, false, true);
       
  1436                 postDownloadComplete();
       
  1437                 // done, write an empty "finished" file to flag completion
       
  1438                 File finished = new File(tmp, "finished");
       
  1439                 new FileOutputStream(finished).close();
       
  1440                 if (isWindowsVista()) {
       
  1441                     if (!moveFileWithBroker(getKernelJREDir() +
       
  1442                             "-bundles\\tmp\\finished")) {
       
  1443                         throw new IOException("unable to create 'finished' file");
       
  1444                     }
       
  1445                 }
       
  1446                 log("DownloadManager.createMergedJars: created " + finished);
       
  1447                 // next JRE startup will move these files into their final
       
  1448                 // locations, as long as no other JREs are running
       
  1449 
       
  1450                 // clean up the local low bundle directory on vista
       
  1451                 if (isWindowsVista()) {
       
  1452                     File tmpDir = getLocalLowTempBundlePath();
       
  1453                     File[] list = tmpDir.listFiles();
       
  1454                     if (list != null) {
       
  1455                         for (int i = 0; i < list.length; i++) {
       
  1456                             list[i].delete();
       
  1457                         }
       
  1458                     }
       
  1459                     tmpDir.delete();
       
  1460                     log("Finished cleanup, " + tmpDir + ".exists(): " + tmpDir.exists());
       
  1461                 }
       
  1462             }
       
  1463             catch (IOException e) {
       
  1464                 log(e);
       
  1465             }
       
  1466         }
       
  1467         while (retry);
       
  1468         log("DownloadManager.createMergedJars: finished");
       
  1469     }
       
  1470 
       
  1471 
       
  1472     private static void shutdown() {
       
  1473         try {
       
  1474             ExecutorService e = Bundle.getThreadPool();
       
  1475             e.shutdown();
       
  1476             e.awaitTermination(60 * 60 * 24, TimeUnit.SECONDS);
       
  1477         }
       
  1478         catch (InterruptedException e) {
       
  1479         }
       
  1480     }
       
  1481 
       
  1482 
       
  1483     // returns the registry key for kernel.debug
       
  1484     static native boolean getDebugKey();
       
  1485 
       
  1486 
       
  1487     // returns the final value for the kernel debug property
       
  1488     public static boolean getDebugProperty(){
       
  1489          /*
       
  1490           * Check registry key value
       
  1491           */
       
  1492          boolean debugEnabled = getDebugKey();
       
  1493 
       
  1494          /*
       
  1495           * Check system property - it should override the registry
       
  1496           * key value.
       
  1497           */
       
  1498          if (System.getProperty(KERNEL_DEBUG_PROPERTY) != null) {
       
  1499              debugEnabled = Boolean.valueOf(
       
  1500                       System.getProperty(KERNEL_DEBUG_PROPERTY));
       
  1501          }
       
  1502          return debugEnabled;
       
  1503 
       
  1504     }
       
  1505 
       
  1506 
       
  1507     /**
       
  1508      * Outputs to the error stream even when System.err has not yet been
       
  1509      * initialized.
       
  1510      */
       
  1511     static void println(String msg) {
       
  1512         if (System.err != null)
       
  1513             System.err.println(msg);
       
  1514         else {
       
  1515             try {
       
  1516                 if (errorStream == null)
       
  1517                     errorStream = new FileOutputStream(FileDescriptor.err);
       
  1518                 errorStream.write((msg +
       
  1519                         System.getProperty("line.separator")).getBytes("utf-8"));
       
  1520             }
       
  1521             catch (IOException e) {
       
  1522                 throw new RuntimeException(e);
       
  1523             }
       
  1524         }
       
  1525     }
       
  1526 
       
  1527 
       
  1528     static void log(String msg) {
       
  1529         if (debug) {
       
  1530             println(msg);
       
  1531             try {
       
  1532                 if (logStream == null) {
       
  1533                     loadJKernelLibrary();
       
  1534                     File path = isWindowsVista() ? getLocalLowTempBundlePath() :
       
  1535                             getBundlePath();
       
  1536                     path = new File(path, "kernel." + getCurrentProcessId() + ".log");
       
  1537                     logStream = new FileOutputStream(path);
       
  1538                 }
       
  1539                 logStream.write((msg +
       
  1540                         System.getProperty("line.separator")).getBytes("utf-8"));
       
  1541                 logStream.flush();
       
  1542             }
       
  1543             catch (IOException e) {
       
  1544                 // ignore
       
  1545             }
       
  1546         }
       
  1547     }
       
  1548 
       
  1549 
       
  1550     static void log(Throwable e) {
       
  1551         ByteArrayOutputStream buffer = new ByteArrayOutputStream();
       
  1552         PrintStream p = new PrintStream(buffer);
       
  1553         e.printStackTrace(p);
       
  1554         p.close();
       
  1555         log(buffer.toString(0));
       
  1556     }
       
  1557 
       
  1558 
       
  1559     /** Dump the contents of a map to System.out. */
       
  1560     private static void printMap(Map/*<String, String>*/ map) {
       
  1561         int size = 0;
       
  1562         Set<Integer> identityHashes = new HashSet<Integer>();
       
  1563         Iterator/*<Map.Entry<String, String>>*/ i = map.entrySet().iterator();
       
  1564         while (i.hasNext()) {
       
  1565             Map.Entry/*<String, String>*/ e = (Map.Entry) i.next();
       
  1566             String key = (String) e.getKey();
       
  1567             String value = (String) e.getValue();
       
  1568             System.out.println(key + ": " + value);
       
  1569             Integer keyHash = Integer.valueOf(System.identityHashCode(key));
       
  1570             if (!identityHashes.contains(keyHash)) {
       
  1571                 identityHashes.add(keyHash);
       
  1572                 size += key.length();
       
  1573             }
       
  1574             Integer valueHash = Integer.valueOf(System.identityHashCode(value));
       
  1575             if (!identityHashes.contains(valueHash)) {
       
  1576                 identityHashes.add(valueHash);
       
  1577                 size += value.length();
       
  1578             }
       
  1579         }
       
  1580         System.out.println(size + " bytes");
       
  1581     }
       
  1582 
       
  1583 
       
  1584     /** Process the "-dumpmaps" command-line argument. */
       
  1585     private static void dumpMaps() throws IOException {
       
  1586         System.out.println("Resources:");
       
  1587         System.out.println("----------");
       
  1588         printMap(getResourceMap());
       
  1589         System.out.println();
       
  1590         System.out.println("Files:");
       
  1591         System.out.println("----------");
       
  1592         printMap(getFileMap());
       
  1593     }
       
  1594 
       
  1595 
       
  1596     /** Process the "-download" command-line argument. */
       
  1597     private static void processDownload(String bundleName) throws IOException {
       
  1598         if (bundleName.equals("all")) {
       
  1599             debug = true;
       
  1600             doBackgroundDownloads(true);
       
  1601             performCompletionIfNeeded();
       
  1602         }
       
  1603         else {
       
  1604             Bundle bundle = Bundle.getBundle(bundleName);
       
  1605             if (bundle == null) {
       
  1606                 println("Unknown bundle: " + bundleName);
       
  1607                 System.exit(1);
       
  1608             }
       
  1609             else
       
  1610                 bundle.install();
       
  1611         }
       
  1612     }
       
  1613 
       
  1614 
       
  1615     static native int getCurrentProcessId();
       
  1616 
       
  1617 
       
  1618     public static void main(String[] arg) throws Exception {
       
  1619         AccessController.checkPermission(new AllPermission());
       
  1620 
       
  1621         boolean valid = false;
       
  1622         if (arg.length == 2 && arg[0].equals("-install")) {
       
  1623             valid = true;
       
  1624             Bundle bundle = new Bundle() {
       
  1625                 protected void updateState() {
       
  1626                     // the bundle path was provided on the command line, so we
       
  1627                     // just claim it has already been "downloaded" to the local
       
  1628                     // filesystem
       
  1629                     state = DOWNLOADED;
       
  1630                 }
       
  1631             };
       
  1632 
       
  1633             File jarPath;
       
  1634             int index = 0;
       
  1635             do {
       
  1636                 index++;
       
  1637                 jarPath = new File(getBundlePath(),
       
  1638                         CUSTOM_PREFIX + index + ".jar");
       
  1639             }
       
  1640             while (jarPath.exists());
       
  1641             bundle.setName(CUSTOM_PREFIX + index);
       
  1642             bundle.setLocalPath(new File(arg[1]));
       
  1643             bundle.setJarPath(jarPath);
       
  1644             bundle.setDeleteOnInstall(false);
       
  1645             bundle.install();
       
  1646         }
       
  1647         else if (arg.length == 2 && arg[0].equals("-download")) {
       
  1648             valid = true;
       
  1649             processDownload(arg[1]);
       
  1650         }
       
  1651         else if (arg.length == 1 && arg[0].equals("-dumpmaps")) {
       
  1652             valid = true;
       
  1653             dumpMaps();
       
  1654         }
       
  1655         else if (arg.length == 2 && arg[0].equals("-sha1")) {
       
  1656             valid = true;
       
  1657             System.out.println(BundleCheck.getInstance(new File(arg[1])));
       
  1658         }
       
  1659         else if (arg.length == 1 && arg[0].equals("-downloadtest")) {
       
  1660             valid = true;
       
  1661             File file = File.createTempFile("download", ".test");
       
  1662             for (;;) {
       
  1663                 file.delete();
       
  1664                 downloadFromURL(getBaseDownloadURL(), file, "URLS", true);
       
  1665                 System.out.println("Downloaded " + file.length() + " bytes");
       
  1666             }
       
  1667         }
       
  1668         if (!valid) {
       
  1669             System.out.println("usage: DownloadManager -install <path>.zip |");
       
  1670             System.out.println("       DownloadManager -download " +
       
  1671                     "<bundle_name> |");
       
  1672             System.out.println("       DownloadManager -dumpmaps");
       
  1673             System.exit(1);
       
  1674         }
       
  1675     }
       
  1676 }