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