001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2009 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS. 026 */ 027package org.opends.quicksetup.webstart; 028 029import java.io.IOException; 030import java.net.MalformedURLException; 031import java.net.URL; 032 033import org.forgerock.i18n.LocalizableMessage; 034import org.forgerock.i18n.slf4j.LocalizedLogger; 035 036import javax.jnlp.DownloadService; 037import javax.jnlp.DownloadServiceListener; 038import javax.jnlp.ServiceManager; 039import javax.jnlp.UnavailableServiceException; 040 041import org.opends.quicksetup.ApplicationException; 042import org.opends.quicksetup.Installation; 043import org.opends.quicksetup.ReturnCode; 044import org.opends.quicksetup.util.Utils; 045import org.opends.server.util.SetupUtils; 046 047import static org.opends.messages.QuickSetupMessages.*; 048import static com.forgerock.opendj.util.OperatingSystem.isWindows; 049import static com.forgerock.opendj.cli.Utils.getThrowableMsg; 050 051/** 052 * This class is used to download the files that have been marked as lazy 053 * in the QuickSetup.jnlp file. 054 * 055 * The global idea is to force the user to download just one jar file 056 * (quicksetup.jar) to display the Web Start installer dialog. Then QuickSetup 057 * will call this class and it will download the jar files. Until this class is 058 * not finished the WebStartInstaller will be on the 059 * ProgressStep.DOWNLOADING step. 060 */ 061public class WebStartDownloader implements DownloadServiceListener { 062 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 063 064 private ApplicationException ex; 065 private boolean isFinished; 066 private int downloadPercentage; 067 private int currentPercMin; 068 private int currentPercMax; 069 private int currentValidatingPercent; 070 private int currentUpgradingPercent; 071 private Status status = Status.DOWNLOADING; 072 private LocalizableMessage summary; 073 074 /** 075 * This enumeration contains the different Status on which 076 * the downloading process of the jars can be. 077 * 078 */ 079 public enum Status 080 { 081 /** 082 * Downloading a jar file. 083 */ 084 DOWNLOADING, 085 /** 086 * Validating a jar file. 087 */ 088 VALIDATING, 089 /** 090 * Upgrading a jar file. 091 */ 092 UPGRADING 093 } 094 095 /** 096 * Creates a default instance. 097 */ 098 public WebStartDownloader() { 099 this.summary = INFO_DOWNLOADING.get(); 100 } 101 102 /** 103 * Starts the downloading of the jar files. If forceDownload is set to 104 * <CODE>true</CODE> the files will be re-downloaded even if they already 105 * are on cache. 106 * 107 * This method does not block the thread that calls it. 108 * 109 * @param forceDownload used to ignore the case and force download. 110 */ 111 public void start(final boolean forceDownload) 112 { 113 isFinished = false; 114 Thread t = new Thread(new Runnable() 115 { 116 @Override 117 public void run() 118 { 119 try 120 { 121 startDownload(forceDownload); 122 } catch (ApplicationException ex) 123 { 124 WebStartDownloader.this.ex = ex; 125 } catch (MalformedURLException mfe) 126 { 127 // This is a bug 128 ex = 129 new ApplicationException(ReturnCode.BUG, 130 getThrowableMsg(INFO_BUG_MSG.get(),mfe), mfe); 131 } catch (IOException ioe) 132 { 133 StringBuilder buf = new StringBuilder(); 134 String[] jars = getJarUrls(); 135 for (int i = 0; i < jars.length; i++) 136 { 137 if (i != 0) 138 { 139 buf.append(","); 140 } 141 buf.append(jars[i]); 142 } 143 ex = new ApplicationException( 144 ReturnCode.DOWNLOAD_ERROR, 145 getThrowableMsg(INFO_DOWNLOADING_ERROR.get(buf), ioe), ioe); 146 } catch (Throwable t) 147 { 148 // This is a bug 149 ex = new ApplicationException(ReturnCode.BUG, 150 getThrowableMsg(INFO_BUG_MSG.get(), t), t); 151 } 152 } 153 }); 154 t.start(); 155 } 156 157 /** 158 * Gets a summary message of the downloader's current progress. 159 * @return String for showing the user progress 160 */ 161 public LocalizableMessage getSummary() { 162 return this.summary; 163 } 164 165 /** 166 * Sets a summary message of the downloader's current progress. 167 * @param summary String for showing the user progress 168 */ 169 public void setSummary(LocalizableMessage summary) { 170 this.summary = summary; 171 } 172 173 /** 174 * Returns <CODE>true</CODE> if the install is finished and 175 * <CODE>false</CODE> otherwise. 176 * @return <CODE>true</CODE> if the install is finished and 177 * <CODE>false</CODE> otherwise. 178 */ 179 public boolean isFinished() 180 { 181 return isFinished; 182 } 183 184 /** 185 * Returns the Status of the current download process. 186 * @return the current status of the download process. 187 */ 188 public Status getStatus() 189 { 190 return status; 191 } 192 193 /** 194 * Returns the current download percentage. 195 * @return the current download percentage. 196 */ 197 public int getDownloadPercentage() 198 { 199 return downloadPercentage; 200 } 201 202 /** 203 * Returns the completed percentage for the file being currently validated. 204 * @return the completed percentage for the file being currently validated. 205 */ 206 public int getCurrentValidatingPercentage() 207 { 208 return currentValidatingPercent; 209 } 210 211 /** 212 * Returns the completed percentage for the file being currently upgraded. 213 * @return the completed percentage for the file being currently upgraded. 214 */ 215 public int getCurrentUpgradingPercentage() 216 { 217 return currentUpgradingPercent; 218 } 219 220 /** 221 * Starts synchronously the downloading on this thread. The thread calling 222 * this method will be blocked. If forceDownload is set to 223 * <CODE>true</CODE> the files will be re-downloaded even if they already 224 * are on cache. 225 * @param forceDownload used to ignore the case and force download. 226 * @throws MalformedURLException if there is an error with the URLs that we 227 * get from the property SetupUtils.LAZY_JAR_URLS 228 * @throws IOException if a network problem occurs. 229 * @throws ApplicationException if the download service is not available. 230 */ 231 private void startDownload(boolean forceDownload) 232 throws IOException, ApplicationException 233 { 234 DownloadService ds; 235 try 236 { 237 ds = 238 (DownloadService) ServiceManager.lookup(Utils.JNLP_SERVICE_NAME); 239 } catch (UnavailableServiceException e) 240 { 241 logger.error(LocalizableMessage.raw("Could not find service: "+ 242 Utils.JNLP_SERVICE_NAME, e)); 243 String setupFile; 244 if (isWindows()) 245 { 246 setupFile = Installation.WINDOWS_SETUP_FILE_NAME; 247 } 248 else 249 { 250 setupFile = Installation.UNIX_SETUP_FILE_NAME; 251 } 252 throw new ApplicationException( 253 ReturnCode.DOWNLOAD_ERROR, 254 getThrowableMsg(INFO_DOWNLOADING_ERROR_NO_SERVICE_FOUND.get( 255 Utils.JNLP_SERVICE_NAME, setupFile), 256 e), e); 257 } 258 259 String[] urls = getJarUrls(); 260 String[] versions = getJarVersions(); 261 262 /* 263 * Calculate the percentages that correspond to each file. 264 * TODO ideally this should be done dynamically, but as this is just 265 * to provide progress, updating this information from time to time can 266 * be enough and does not complexify the build process. 267 */ 268 int[] percentageMax = new int[urls.length]; 269 int[] ratios = new int[urls.length]; 270 int totalRatios = 0; 271 for (int i=0; i<percentageMax.length; i++) 272 { 273 int ratio; 274 if (urls[i].endsWith("OpenDS.jar")) 275 { 276 ratio = 23; 277 } 278 else if (urls[i].endsWith("je.jar")) 279 { 280 ratio = 11; 281 } 282 else if (urls[i].endsWith("zipped.jar")) 283 { 284 ratio = 110; 285 } 286 else if (urls[i].endsWith("aspectjrt.jar")) 287 { 288 ratio = 10; 289 } 290 else 291 { 292 ratio = 100 / urls.length; 293 } 294 ratios[i] = ratio; 295 totalRatios += ratio; 296 } 297 298 for (int i=0; i<percentageMax.length; i++) 299 { 300 int r = 0; 301 for (int j=0; j<=i; j++) 302 { 303 r += ratios[j]; 304 } 305 percentageMax[i] = (100 * r)/totalRatios; 306 } 307 308 309 for (int i = 0; i < urls.length && getException() == null; i++) 310 { 311 if (i == 0) 312 { 313 currentPercMin = 0; 314 } 315 else { 316 currentPercMin = percentageMax[i-1]; 317 } 318 currentPercMax = percentageMax[i]; 319 320 // determine if a particular resource is cached 321 String sUrl = urls[i]; 322 String version = versions[i]; 323 324 URL url = new URL(sUrl); 325 boolean cached = ds.isResourceCached(url, version); 326 327 if (cached && forceDownload) 328 { 329 try 330 { 331 ds.removeResource(url, version); 332 } catch (IOException ioe) 333 { 334 } 335 cached = false; 336 } 337 338 if (!cached) 339 { 340 // if not in the cache load the resource into the cache 341 ds.loadResource(url, version, this); 342 } 343 downloadPercentage = currentPercMax; 344 } 345 isFinished = true; 346 } 347 348 /** 349 * Returns the ApplicationException that has occurred during the download or 350 * <CODE>null</CODE> if no exception occurred. 351 * @return the ApplicationException that has occurred during the download or 352 * <CODE>null</CODE> if no exception occurred. 353 */ 354 public ApplicationException getException() 355 { 356 return ex; 357 } 358 359 /** {@inheritDoc} */ 360 @Override 361 public void downloadFailed(URL url, String version) 362 { 363 ex = 364 new ApplicationException( 365 ReturnCode.DOWNLOAD_ERROR, 366 INFO_DOWNLOADING_ERROR.get(url), null); 367 } 368 369 /** {@inheritDoc} */ 370 @Override 371 public void progress(URL url, String version, long readSoFar, long total, 372 int overallPercent) 373 { 374 if (overallPercent >= 0) 375 { 376 downloadPercentage = getPercentage(overallPercent); 377 } 378 status = Status.DOWNLOADING; 379 } 380 381 /** {@inheritDoc} */ 382 @Override 383 public void upgradingArchive(URL url, String version, int patchPercent, 384 int overallPercent) 385 { 386 currentUpgradingPercent = overallPercent; 387 status = Status.UPGRADING; 388 } 389 390 /** {@inheritDoc} */ 391 @Override 392 public void validating(URL url, String version, long entry, long total, 393 int overallPercent) 394 { 395 if (total > 0) 396 { 397 currentValidatingPercent = (int)((100 * entry) / total); 398 } 399 else { 400 currentValidatingPercent = 0; 401 } 402 403 status = Status.VALIDATING; 404 } 405 406 /** 407 * Returns the jar files in a String[] from the System properties. 408 * @return the jar files from the System properties. 409 */ 410 private String[] getJarUrls() 411 { 412 String jars = System.getProperty(SetupUtils.LAZY_JAR_URLS); 413 return jars.split(" "); 414 } 415 416 417 /** 418 * Returns the downloaded percentage based on how much of the current jar file 419 * has been downloaded. 420 * @param currentJarRatio the download ratio of the jar file that is being 421 * currently downloaded. 422 * @return the downloaded percentage based on how much of the current jar file 423 * has been downloaded. 424 */ 425 private int getPercentage(int currentJarRatio) 426 { 427 return currentPercMin 428 + (currentJarRatio * (currentPercMax - currentPercMin) / 100); 429 } 430 431 /** 432 * Returns the java jar versions in a String[]. Currently just returns some 433 * null strings. 434 * @return the java jar versions in a String[]. 435 */ 436 private String[] getJarVersions() 437 { 438 return new String[getJarUrls().length]; 439 } 440 441}