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-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.quicksetup.installer.webstart; 028 029import java.io.File; 030import java.io.IOException; 031import java.io.InputStream; 032import java.io.PrintStream; 033import java.util.ArrayList; 034import java.util.HashMap; 035import java.util.List; 036import java.util.Map; 037 038import org.forgerock.i18n.LocalizableMessage; 039import org.forgerock.i18n.slf4j.LocalizedLogger; 040 041import org.opends.quicksetup.ApplicationException; 042import org.opends.quicksetup.LicenseFile; 043import org.opends.quicksetup.ReturnCode; 044import org.opends.quicksetup.ProgressStep; 045import org.opends.quicksetup.Installation; 046import org.opends.quicksetup.installer.Installer; 047import org.opends.quicksetup.installer.InstallProgressStep; 048import org.opends.quicksetup.util.Utils; 049import org.opends.quicksetup.util.ZipExtractor; 050import org.opends.quicksetup.util.ServerController; 051import org.opends.quicksetup.util.FileManager; 052import org.opends.server.util.SetupUtils; 053 054import static org.opends.messages.QuickSetupMessages.*; 055import static com.forgerock.opendj.util.OperatingSystem.isWindows; 056import static com.forgerock.opendj.cli.Utils.getThrowableMsg; 057 058/** 059 * This is an implementation of the Installer class that is used to install 060 * the Directory Server using Web Start. 061 * 062 * It just takes a UserData object and based on that installs OpenDS. 063 * 064 * 065 * This object has as parameter a WebStartDownloader object that is downloading 066 * some jar files. Until the WebStartDownloader has not finished downloading 067 * the jar files will be on the ProgressStep.DOWNLOADING step because 068 * we require all the jar files to be downloaded in order to install and 069 * configure the Directory Server. 070 * 071 * Based on the Java properties set through the QuickSetup.jnlp file this 072 * class will retrieve the zip file containing the install, unzip it and extract 073 * it in the path specified by the user and that is contained in the 074 * UserData object. 075 * 076 * 077 * When there is an update during the installation it will notify the 078 * ProgressUpdateListener objects that have been added to it. The notification 079 * will send a ProgressUpdateEvent. 080 * 081 * This class is supposed to be fully independent of the graphical layout. 082 */ 083public class WebStartInstaller extends Installer { 084 private final Map<ProgressStep, Integer> hmRatio = new HashMap<>(); 085 private final Map<ProgressStep, LocalizableMessage> hmSummary = new HashMap<>(); 086 087 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 088 089 /** 090 * WebStartInstaller constructor. 091 */ 092 public WebStartInstaller() 093 { 094 initLoader(); 095 setCurrentProgressStep(InstallProgressStep.NOT_STARTED); 096 } 097 098 /** Actually performs the install in this thread. The thread is blocked. */ 099 @Override 100 public void run() 101 { 102 initMaps(); 103 PrintStream origErr = System.err; 104 PrintStream origOut = System.out; 105 boolean downloadedBits = false; 106 try 107 { 108 System.setErr(getApplicationErrorStream()); 109 System.setOut(getApplicationOutputStream()); 110 111 setCurrentProgressStep(InstallProgressStep.DOWNLOADING); 112 113 notifyListenersOfLog(); 114 notifyListeners(getLineBreak()); 115 116 checkAbort(); 117 118 InputStream in = 119 getZipInputStream(getRatio(InstallProgressStep.EXTRACTING)); 120 121 setCurrentProgressStep(InstallProgressStep.EXTRACTING); 122 if (isVerbose()) 123 { 124 notifyListeners(getTaskSeparator()); 125 } 126 127 checkAbort(); 128 129 createParentDirectoryIfRequired(); 130 extractZipFiles(in, getRatio(InstallProgressStep.EXTRACTING), 131 getRatio(InstallProgressStep.CONFIGURING_SERVER)); 132 downloadedBits = true; 133 134 try 135 { 136 in.close(); 137 } 138 catch (Throwable t) 139 { 140 logger.info(LocalizableMessage.raw("Error closing zip input stream: "+t, t)); 141 } 142 143 checkAbort(); 144 145 setCurrentProgressStep(InstallProgressStep.CONFIGURING_SERVER); 146 if (isVerbose()) 147 { 148 notifyListeners(getTaskSeparator()); 149 } 150 configureServer(); 151 152 checkAbort(); 153 154 // create license accepted file 155 LicenseFile.createFileLicenseApproved(getInstallationPath()); 156 157 createData(); 158 159 checkAbort(); 160 161 if (isWindows() && getUserData().getEnableWindowsService()) 162 { 163 if (isVerbose()) 164 { 165 notifyListeners(getTaskSeparator()); 166 } 167 setCurrentProgressStep(InstallProgressStep.ENABLING_WINDOWS_SERVICE); 168 enableWindowsService(); 169 checkAbort(); 170 } 171 172 if (mustStart()) 173 { 174 if (isVerbose()) 175 { 176 notifyListeners(getTaskSeparator()); 177 } 178 setCurrentProgressStep(InstallProgressStep.STARTING_SERVER); 179 PointAdder pointAdder = new PointAdder(); 180 if (!isVerbose()) 181 { 182 notifyListeners(getFormattedProgress( 183 INFO_PROGRESS_STARTING_NON_VERBOSE.get())); 184 pointAdder.start(); 185 } 186 try 187 { 188 new ServerController(this).startServer(!isStartVerbose()); 189 } 190 finally 191 { 192 if (!isVerbose()) 193 { 194 pointAdder.stop(); 195 } 196 } 197 if (!isVerbose()) 198 { 199 notifyListeners(getFormattedDoneWithLineBreak()); 200 } 201 else 202 { 203 notifyListeners(getLineBreak()); 204 } 205 checkAbort(); 206 } 207 208 if (mustCreateAds()) 209 { 210 if (isVerbose()) 211 { 212 notifyListeners(getTaskSeparator()); 213 } 214 setCurrentProgressStep(InstallProgressStep.CONFIGURING_ADS); 215 updateADS(); 216 checkAbort(); 217 } 218 219 if (mustConfigureReplication()) 220 { 221 if (isVerbose()) 222 { 223 notifyListeners(getTaskSeparator()); 224 } 225 setCurrentProgressStep(InstallProgressStep.CONFIGURING_REPLICATION); 226 createReplicatedBackendsIfRequired(); 227 configureReplication(); 228 checkAbort(); 229 } 230 231 if (mustInitializeSuffixes()) 232 { 233 if (isVerbose()) 234 { 235 notifyListeners(getTaskSeparator()); 236 } 237 setCurrentProgressStep( 238 InstallProgressStep.INITIALIZE_REPLICATED_SUFFIXES); 239 initializeSuffixes(); 240 checkAbort(); 241 } 242 243 if (mustStop()) 244 { 245 if (isVerbose()) 246 { 247 notifyListeners(getTaskSeparator()); 248 } 249 setCurrentProgressStep(InstallProgressStep.STOPPING_SERVER); 250 if (!isVerbose()) 251 { 252 notifyListeners(getFormattedWithPoints( 253 INFO_PROGRESS_STOPPING_NON_VERBOSE.get())); 254 } 255 new ServerController(this).stopServer(!isVerbose()); 256 if (!isVerbose()) 257 { 258 notifyListeners(getFormattedDoneWithLineBreak()); 259 } 260 } 261 262 checkAbort(); 263 updateSummaryWithServerState(hmSummary, false); 264 setCurrentProgressStep(InstallProgressStep.FINISHED_SUCCESSFULLY); 265 notifyListeners(null); 266 267 } catch (ApplicationException ex) 268 { 269 if (ReturnCode.CANCELED.equals(ex.getType())) { 270 uninstall(downloadedBits); 271 272 setCurrentProgressStep(InstallProgressStep.FINISHED_CANCELED); 273 notifyListeners(null); 274 } else { 275 // Stop the server if necessary 276 Installation installation = getInstallation(); 277 if (installation.getStatus().isServerRunning()) { 278 try { 279 if (!isVerbose()) 280 { 281 notifyListeners(getFormattedWithPoints( 282 INFO_PROGRESS_STOPPING_NON_VERBOSE.get())); 283 } 284 new ServerController(installation).stopServer(!isVerbose()); 285 if (!isVerbose()) 286 { 287 notifyListeners(getFormattedDoneWithLineBreak()); 288 } 289 } catch (Throwable t) { 290 logger.info(LocalizableMessage.raw("error stopping server", t)); 291 } 292 } 293 notifyListeners(getLineBreak()); 294 updateSummaryWithServerState(hmSummary, false); 295 setCurrentProgressStep(InstallProgressStep.FINISHED_WITH_ERROR); 296 LocalizableMessage html = getFormattedError(ex, true); 297 notifyListeners(html); 298 logger.error(LocalizableMessage.raw("Error installing.", ex)); 299 notifyListeners(getLineBreak()); 300 notifyListenersOfLogAfterError(); 301 } 302 } 303 catch (Throwable t) 304 { 305 // Stop the server if necessary 306 Installation installation = getInstallation(); 307 if (installation.getStatus().isServerRunning()) { 308 try { 309 new ServerController(installation).stopServer(true); 310 } catch (Throwable t2) { 311 logger.info(LocalizableMessage.raw("error stopping server", t2)); 312 } 313 } 314 notifyListeners(getLineBreak()); 315 updateSummaryWithServerState(hmSummary, false); 316 setCurrentProgressStep(InstallProgressStep.FINISHED_WITH_ERROR); 317 ApplicationException ex = new ApplicationException( 318 ReturnCode.BUG, 319 getThrowableMsg(INFO_BUG_MSG.get(), t), t); 320 LocalizableMessage msg = getFormattedError(ex, true); 321 notifyListeners(msg); 322 logger.error(LocalizableMessage.raw("Error installing.", t)); 323 notifyListeners(getLineBreak()); 324 notifyListenersOfLogAfterError(); 325 } 326 System.setErr(origErr); 327 System.setOut(origOut); 328 } 329 330 /** {@inheritDoc} */ 331 @Override 332 public Integer getRatio(ProgressStep status) 333 { 334 return hmRatio.get(status); 335 } 336 337 /** {@inheritDoc} */ 338 @Override 339 public LocalizableMessage getSummary(ProgressStep status) 340 { 341 LocalizableMessage summary; 342 if (InstallProgressStep.DOWNLOADING.equals(status)) { 343 summary = loader.getSummary(); 344 } else { 345 summary = hmSummary.get(status); 346 } 347 return summary; 348 } 349 350 /** 351 * Initialize the different map used in this class. 352 * 353 */ 354 private void initMaps() 355 { 356 initSummaryMap(hmSummary, false); 357 358 /* 359 * hmTime contains the relative time that takes for each task to be 360 * accomplished. For instance if downloading takes twice the time of 361 * extracting, the value for downloading will be the double of the value for 362 * extracting. 363 */ 364 Map<ProgressStep, Integer> hmTime = new HashMap<>(); 365 hmTime.put(InstallProgressStep.DOWNLOADING, 30); 366 hmTime.put(InstallProgressStep.EXTRACTING, 15); 367 hmTime.put(InstallProgressStep.CONFIGURING_SERVER, 5); 368 hmTime.put(InstallProgressStep.CREATING_BASE_ENTRY, 10); 369 hmTime.put(InstallProgressStep.IMPORTING_LDIF, 20); 370 hmTime.put(InstallProgressStep.IMPORTING_AUTOMATICALLY_GENERATED, 20); 371 hmTime.put(InstallProgressStep.CONFIGURING_REPLICATION, 10); 372 hmTime.put(InstallProgressStep.ENABLING_WINDOWS_SERVICE, 5); 373 hmTime.put(InstallProgressStep.STARTING_SERVER, 10); 374 hmTime.put(InstallProgressStep.STOPPING_SERVER, 5); 375 hmTime.put(InstallProgressStep.CONFIGURING_ADS, 5); 376 hmTime.put(InstallProgressStep.INITIALIZE_REPLICATED_SUFFIXES, 25); 377 378 int totalTime = 0; 379 List<InstallProgressStep> steps = new ArrayList<>(); 380 totalTime += hmTime.get(InstallProgressStep.DOWNLOADING); 381 steps.add(InstallProgressStep.DOWNLOADING); 382 totalTime += hmTime.get(InstallProgressStep.EXTRACTING); 383 steps.add(InstallProgressStep.EXTRACTING); 384 totalTime += hmTime.get(InstallProgressStep.CONFIGURING_SERVER); 385 steps.add(InstallProgressStep.CONFIGURING_SERVER); 386 387 if (createNotReplicatedSuffix()) 388 { 389 switch (getUserData().getNewSuffixOptions().getType()) 390 { 391 case CREATE_BASE_ENTRY: 392 steps.add(InstallProgressStep.CREATING_BASE_ENTRY); 393 totalTime += hmTime.get(InstallProgressStep.CREATING_BASE_ENTRY); 394 break; 395 case IMPORT_FROM_LDIF_FILE: 396 steps.add(InstallProgressStep.IMPORTING_LDIF); 397 totalTime += hmTime.get(InstallProgressStep.IMPORTING_LDIF); 398 break; 399 case IMPORT_AUTOMATICALLY_GENERATED_DATA: 400 steps.add(InstallProgressStep.IMPORTING_AUTOMATICALLY_GENERATED); 401 totalTime +=hmTime.get( 402 InstallProgressStep.IMPORTING_AUTOMATICALLY_GENERATED); 403 break; 404 } 405 } 406 407 if (isWindows() && getUserData().getEnableWindowsService()) 408 { 409 totalTime += hmTime.get(InstallProgressStep.ENABLING_WINDOWS_SERVICE); 410 steps.add(InstallProgressStep.ENABLING_WINDOWS_SERVICE); 411 } 412 if (mustStart()) 413 { 414 totalTime += hmTime.get(InstallProgressStep.STARTING_SERVER); 415 steps.add(InstallProgressStep.STARTING_SERVER); 416 } 417 418 if (mustCreateAds()) 419 { 420 totalTime += hmTime.get(InstallProgressStep.CONFIGURING_ADS); 421 steps.add(InstallProgressStep.CONFIGURING_ADS); 422 } 423 424 if (mustConfigureReplication()) 425 { 426 steps.add(InstallProgressStep.CONFIGURING_REPLICATION); 427 totalTime += hmTime.get(InstallProgressStep.CONFIGURING_REPLICATION); 428 } 429 430 if (mustInitializeSuffixes()) 431 { 432 totalTime += hmTime.get( 433 InstallProgressStep.INITIALIZE_REPLICATED_SUFFIXES); 434 steps.add(InstallProgressStep.INITIALIZE_REPLICATED_SUFFIXES); 435 } 436 437 if (mustStop()) 438 { 439 totalTime += hmTime.get(InstallProgressStep.STOPPING_SERVER); 440 steps.add(InstallProgressStep.STOPPING_SERVER); 441 } 442 443 int cumulatedTime = 0; 444 for (InstallProgressStep s : steps) 445 { 446 Integer statusTime = hmTime.get(s); 447 hmRatio.put(s, (100 * cumulatedTime) / totalTime); 448 if (statusTime != null) 449 { 450 cumulatedTime += statusTime; 451 } 452 } 453 454 hmRatio.put(InstallProgressStep.FINISHED_SUCCESSFULLY, 100); 455 hmRatio.put(InstallProgressStep.FINISHED_CANCELED, 100); 456 hmRatio.put(InstallProgressStep.FINISHED_WITH_ERROR, 100); 457 } 458 459 private InputStream getZipInputStream(Integer maxRatio) 460 throws ApplicationException { 461 notifyListeners(getFormattedWithPoints(INFO_PROGRESS_DOWNLOADING.get())); 462 463 waitForLoader(maxRatio); 464 465 String zipName = getZipFileName(); 466 InputStream in = 467 Installer.class.getClassLoader().getResourceAsStream(zipName); 468 469 if (in == null) 470 { 471 throw new ApplicationException( 472 ReturnCode.DOWNLOAD_ERROR, 473 INFO_ERROR_ZIPINPUTSTREAMNULL.get(zipName), null); 474 } 475 476 notifyListeners(getFormattedDoneWithLineBreak()); 477 return in; 478 } 479 480 /** 481 * Creates the parent Directory for the server location if it does not exist. 482 * @throws ApplicationException if something goes wrong. 483 */ 484 private void createParentDirectoryIfRequired() throws ApplicationException 485 { 486 String serverLocation = getUserData().getServerLocation(); 487 if (!Utils.parentDirectoryExists(serverLocation)) 488 { 489 File f = new File(serverLocation); 490 String parent = f.getParent(); 491 try 492 { 493 if (!Utils.createDirectory(parent)) 494 { 495 throw new ApplicationException( 496 ReturnCode.FILE_SYSTEM_ACCESS_ERROR, 497 INFO_ERROR_COULD_NOT_CREATE_PARENT_DIR.get(parent), null); 498 } 499 } 500 catch (IOException ioe) 501 { 502 throw new ApplicationException( 503 ReturnCode.FILE_SYSTEM_ACCESS_ERROR, 504 INFO_ERROR_COULD_NOT_CREATE_PARENT_DIR.get(parent), 505 ioe); 506 } 507 } 508 } 509 510 /** 511 * This method extracts the zip file. 512 * @param is the input stream with the contents of the zip file. 513 * @param minRatio the value of the ratio in the install that corresponds to 514 * the moment where we start extracting the zip files. Used to update 515 * properly the install progress ratio. 516 * @param maxRatio the value of the ratio in the installation that corresponds 517 * to the moment where we finished extracting the last zip file. Used to 518 * update properly the install progress ratio. 519 * @throws ApplicationException if an error occurs. 520 */ 521 private void extractZipFiles(InputStream is, int minRatio, int maxRatio) 522 throws ApplicationException { 523 ZipExtractor extractor = 524 new ZipExtractor(is, minRatio, maxRatio, 525 Utils.getNumberZipEntries(), 526 getZipFileName(), 527 this); 528 extractor.extract(getUserData().getServerLocation()); 529 } 530 531 /** 532 * Returns the name of the zip file name that contains all the installation. 533 * @return the name of the zip file name that contains all the installation. 534 */ 535 private String getZipFileName() 536 { 537 // Passed as a java option in the JNLP file 538 return System.getProperty(SetupUtils.ZIP_FILE_NAME); 539 } 540 541 /** 542 * Uninstall what has already been installed. 543 * @param downloadedBits whether the bits were downloaded or not. 544 */ 545 private void uninstall(boolean downloadedBits) { 546 if (downloadedBits) 547 { 548 notifyListeners(getTaskSeparator()); 549 if (!isVerbose()) 550 { 551 notifyListeners(getFormattedWithPoints(INFO_PROGRESS_CANCELING.get())); 552 } 553 else 554 { 555 notifyListeners( 556 getFormattedProgressWithLineBreak(INFO_SUMMARY_CANCELING.get())); 557 } 558 Installation installation = getInstallation(); 559 FileManager fm = new FileManager(this); 560 561 // Stop the server if necessary 562 if (installation.getStatus().isServerRunning()) { 563 try { 564 new ServerController(installation).stopServer(true); 565 } catch (ApplicationException e) { 566 logger.info(LocalizableMessage.raw("error stopping server", e)); 567 } 568 } 569 570 uninstallServices(); 571 572 try { 573 fm.deleteRecursively(installation.getRootDirectory(), null, 574 FileManager.DeletionPolicy.DELETE_ON_EXIT_IF_UNSUCCESSFUL); 575 } catch (ApplicationException e) { 576 logger.info(LocalizableMessage.raw("error deleting files", e)); 577 } 578 } 579 else 580 { 581 if (!isVerbose()) 582 { 583 notifyListeners(getFormattedWithPoints(INFO_PROGRESS_CANCELING.get())); 584 } 585 else 586 { 587 notifyListeners( 588 getFormattedProgressWithLineBreak(INFO_SUMMARY_CANCELING.get())); 589 } 590 File serverRoot = new File(getUserData().getServerLocation()); 591 if (serverRoot.exists()) 592 { 593 FileManager fm = new FileManager(this); 594 try { 595 fm.deleteRecursively(serverRoot, null, 596 FileManager.DeletionPolicy.DELETE_ON_EXIT_IF_UNSUCCESSFUL); 597 } catch (ApplicationException e) { 598 logger.info(LocalizableMessage.raw("error deleting files", e)); 599 } 600 } 601 } 602 if (!isVerbose()) 603 { 604 notifyListeners(getFormattedDoneWithLineBreak()); 605 } 606 } 607 608 /** {@inheritDoc} */ 609 @Override 610 public String getInstallationPath() 611 { 612 return getUserData().getServerLocation(); 613 } 614 615 /** {@inheritDoc} */ 616 @Override 617 public String getInstancePath() 618 { 619 return getUserData().getServerLocation(); 620 } 621}