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.offline;
028
029import org.forgerock.i18n.LocalizableMessage;
030import static org.opends.messages.QuickSetupMessages.*;
031import static com.forgerock.opendj.util.OperatingSystem.isWindows;
032import static com.forgerock.opendj.cli.Utils.getThrowableMsg;
033
034import java.io.PrintStream;
035import java.io.File;
036import java.util.ArrayList;
037import java.util.HashMap;
038import java.util.List;
039import java.util.Map;
040
041import org.forgerock.i18n.slf4j.LocalizedLogger;
042import java.security.KeyStoreException;
043
044import org.opends.quicksetup.ApplicationException;
045import org.opends.quicksetup.LicenseFile;
046import org.opends.quicksetup.ReturnCode;
047import org.opends.quicksetup.ProgressStep;
048import org.opends.quicksetup.Installation;
049import org.opends.quicksetup.SecurityOptions;
050import org.opends.quicksetup.installer.Installer;
051import org.opends.quicksetup.installer.InstallProgressStep;
052import org.opends.quicksetup.util.Utils;
053import org.opends.quicksetup.util.ServerController;
054import org.opends.quicksetup.util.FileManager;
055import org.opends.server.util.CertificateManager;
056
057/**
058 * This is an implementation of the Installer class that is used to install
059 * the Directory Server from a zip file.  The installer assumes that the zip
060 * file contents have been unzipped.
061 *
062 * It just takes a UserData object and based on that installs OpenDS.
063 *
064 * When there is an update during the installation it will notify the
065 * ProgressUpdateListener objects that have been added to it.  The notification
066 * will send a ProgressUpdateEvent.
067 *
068 * This class is supposed to be fully independent of the graphical layout.
069 *
070 */
071public class OfflineInstaller extends Installer
072{
073  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
074
075  /** This map contains the ratio associated with each step. */
076  private final Map<ProgressStep, Integer> hmRatio = new HashMap<>();
077  /** This map contains the summary associated with each step. */
078  private final Map<ProgressStep, LocalizableMessage> hmSummary = new HashMap<>();
079
080  private ApplicationException runError;
081
082  /**
083   * Actually performs the install in this thread.  The thread is blocked.
084   */
085  @Override
086  public void run()
087  {
088    runError = null;
089    PrintStream origErr = System.err;
090    PrintStream origOut = System.out;
091    try
092    {
093      initMaps();
094
095      System.setErr(getApplicationErrorStream());
096      System.setOut(getApplicationOutputStream());
097
098      checkAbort();
099
100      setCurrentProgressStep(InstallProgressStep.CONFIGURING_SERVER);
101
102      notifyListenersOfLog();
103      notifyListeners(getLineBreak());
104
105      configureServer();
106
107      checkAbort();
108
109      // create license accepted file
110      LicenseFile.createFileLicenseApproved(getInstallationPath());
111
112      checkAbort() ;
113
114      createData();
115
116      checkAbort();
117
118      if (isWindows() && getUserData().getEnableWindowsService())
119      {
120        if (isVerbose())
121        {
122          notifyListeners(getTaskSeparator());
123        }
124        setCurrentProgressStep(InstallProgressStep.ENABLING_WINDOWS_SERVICE);
125        enableWindowsService();
126        checkAbort();
127      }
128
129      if (mustStart())
130      {
131        if (isStartVerbose())
132        {
133          notifyListeners(getTaskSeparator());
134        }
135        setCurrentProgressStep(InstallProgressStep.STARTING_SERVER);
136        PointAdder pointAdder = new PointAdder();
137        if (!isStartVerbose())
138        {
139          notifyListeners(getFormattedProgress(
140              INFO_PROGRESS_STARTING_NON_VERBOSE.get()));
141          pointAdder.start();
142        }
143        try
144        {
145          new ServerController(this).startServer(!isStartVerbose());
146        }
147        finally
148        {
149          if (!isStartVerbose())
150          {
151            pointAdder.stop();
152          }
153        }
154        if (!isStartVerbose())
155        {
156          notifyListeners(getFormattedDoneWithLineBreak());
157        }
158        else
159        {
160          notifyListeners(getLineBreak());
161        }
162        checkAbort();
163      }
164
165      if (mustCreateAds())
166      {
167        if (isVerbose())
168        {
169          notifyListeners(getTaskSeparator());
170        }
171        setCurrentProgressStep(InstallProgressStep.CONFIGURING_ADS);
172        updateADS();
173        checkAbort();
174      }
175
176      if (mustConfigureReplication())
177      {
178        if (isVerbose())
179        {
180          notifyListeners(getTaskSeparator());
181        }
182        setCurrentProgressStep(InstallProgressStep.CONFIGURING_REPLICATION);
183        createReplicatedBackendsIfRequired();
184        configureReplication();
185        checkAbort();
186      }
187
188      if (mustInitializeSuffixes())
189      {
190        if (isVerbose())
191        {
192          notifyListeners(getTaskSeparator());
193        }
194        setCurrentProgressStep(
195            InstallProgressStep.INITIALIZE_REPLICATED_SUFFIXES);
196        initializeSuffixes();
197        checkAbort();
198      }
199
200      if (mustStop())
201      {
202        if (isVerbose())
203        {
204          notifyListeners(getTaskSeparator());
205        }
206        setCurrentProgressStep(InstallProgressStep.STOPPING_SERVER);
207        if (!isVerbose())
208        {
209          notifyListeners(getFormattedWithPoints(
210              INFO_PROGRESS_STOPPING_NON_VERBOSE.get()));
211        }
212        new ServerController(this).stopServer(!isVerbose());
213        if (!isVerbose())
214        {
215          notifyListeners(getFormattedDoneWithLineBreak());
216        }
217      }
218
219      checkAbort();
220      updateSummaryWithServerState(hmSummary, true);
221      setCurrentProgressStep(InstallProgressStep.FINISHED_SUCCESSFULLY);
222      notifyListeners(null);
223
224    } catch (ApplicationException ex)
225    {
226      logger.error(LocalizableMessage.raw("Caught exception: "+ex, ex));
227      if (ReturnCode.CANCELED.equals(ex.getType())) {
228        uninstall();
229        setCurrentProgressStep(InstallProgressStep.FINISHED_CANCELED);
230        notifyListeners(null);
231      } else {
232        // Stop the server if necessary
233        Installation installation = getInstallation();
234        if (installation.getStatus().isServerRunning()) {
235          try {
236            if (!isVerbose())
237            {
238              notifyListeners(getFormattedWithPoints(
239                  INFO_PROGRESS_STOPPING_NON_VERBOSE.get()));
240            }
241            new ServerController(installation).stopServer(!isVerbose());
242            if (!isVerbose())
243            {
244              notifyListeners(getFormattedDoneWithLineBreak());
245            }
246          } catch (Throwable t) {
247            logger.info(LocalizableMessage.raw("error stopping server", t));
248          }
249        }
250        notifyListeners(getLineBreak());
251        updateSummaryWithServerState(hmSummary, true);
252        setCurrentProgressStep(InstallProgressStep.FINISHED_WITH_ERROR);
253        LocalizableMessage html = getFormattedError(ex, true);
254        notifyListeners(html);
255        logger.error(LocalizableMessage.raw("Error installing.", ex));
256        notifyListeners(getLineBreak());
257        notifyListenersOfLogAfterError();
258      }
259      runError = ex;
260    }
261    catch (Throwable t)
262    {
263      // Stop the server if necessary
264      Installation installation = getInstallation();
265      if (installation.getStatus().isServerRunning()) {
266        try {
267          if (!isVerbose())
268          {
269            notifyListeners(getFormattedWithPoints(
270                INFO_PROGRESS_STOPPING_NON_VERBOSE.get()));
271          }
272          new ServerController(installation).stopServer(!isVerbose());
273          if (!isVerbose())
274          {
275            notifyListeners(getFormattedDoneWithLineBreak());
276          }
277        } catch (Throwable t2) {
278          logger.info(LocalizableMessage.raw("error stopping server", t2));
279        }
280      }
281      notifyListeners(getLineBreak());
282      updateSummaryWithServerState(hmSummary, true);
283      setCurrentProgressStep(InstallProgressStep.FINISHED_WITH_ERROR);
284      ApplicationException ex = new ApplicationException(
285          ReturnCode.BUG,
286          getThrowableMsg(INFO_BUG_MSG.get(), t), t);
287      LocalizableMessage msg = getFormattedError(ex, true);
288      notifyListeners(msg);
289      logger.error(LocalizableMessage.raw("Error installing.", t));
290      notifyListeners(getLineBreak());
291      notifyListenersOfLogAfterError();
292      runError = ex;
293    }
294    finally
295    {
296      System.setErr(origErr);
297      System.setOut(origOut);
298    }
299  }
300
301  /** {@inheritDoc} */
302  @Override
303  public Integer getRatio(ProgressStep status)
304  {
305    return hmRatio.get(status);
306  }
307
308  /** {@inheritDoc} */
309  @Override
310  public LocalizableMessage getSummary(ProgressStep status)
311  {
312    return hmSummary.get(status);
313  }
314
315  /**
316   * Returns the exception from the run() method, if any.
317   * @return the ApplicationException raised during the run() method, if any.
318   *         null otherwise.
319   */
320  public ApplicationException getRunError()
321  {
322    return runError;
323  }
324
325  /**
326   * Called when the user elects to cancel this operation.
327   */
328  protected void uninstall() {
329
330    notifyListeners(getTaskSeparator());
331    if (!isVerbose())
332    {
333      notifyListeners(getFormattedWithPoints(INFO_PROGRESS_CANCELING.get()));
334    }
335    else
336    {
337      notifyListeners(
338          getFormattedProgressWithLineBreak(INFO_SUMMARY_CANCELING.get()));
339    }
340    Installation installation = getInstallation();
341    FileManager fm = new FileManager(this);
342
343    // Stop the server if necessary
344    if (installation.getStatus().isServerRunning()) {
345      try {
346        if (!isVerbose())
347        {
348          notifyListeners(getFormattedWithPoints(
349              INFO_PROGRESS_STOPPING_NON_VERBOSE.get()));
350        }
351        new ServerController(installation).stopServer(!isVerbose());
352        if (!isVerbose())
353        {
354          notifyListeners(getFormattedDoneWithLineBreak());
355        }
356      } catch (ApplicationException e) {
357        logger.info(LocalizableMessage.raw("error stopping server", e));
358      }
359    }
360
361    uninstallServices();
362
363    // Revert to the base configuration
364    try {
365      File newConfig = fm.copy(installation.getBaseConfigurationFile(),
366                               installation.getConfigurationDirectory(),
367                               /*overwrite=*/true);
368      fm.rename(newConfig, installation.getCurrentConfigurationFile());
369
370    } catch (ApplicationException ae) {
371      logger.info(LocalizableMessage.raw("failed to restore base configuration", ae));
372    }
373
374    // Cleanup SSL if necessary
375    SecurityOptions sec = getUserData().getSecurityOptions();
376    if (sec.getEnableSSL() || sec.getEnableStartTLS()) {
377      if (SecurityOptions.CertificateType.SELF_SIGNED_CERTIFICATE.equals(
378              sec.getCertificateType())) {
379        CertificateManager cm = new CertificateManager(
380            getSelfSignedKeystorePath(),
381            CertificateManager.KEY_STORE_TYPE_JKS,
382            getSelfSignedCertificatePwd());
383        try {
384          for (String alias : SELF_SIGNED_CERT_ALIASES)
385          {
386            if (cm.aliasInUse(alias))
387            {
388              cm.removeCertificate(alias);
389            }
390          }
391        } catch (KeyStoreException e) {
392          logger.info(LocalizableMessage.raw("Error deleting self signed certification", e));
393        }
394      }
395
396      File keystore = new File(installation.getConfigurationDirectory(),
397              "keystore");
398      if (keystore.exists()) {
399        try {
400          fm.delete(keystore);
401        } catch (ApplicationException e) {
402          logger.info(LocalizableMessage.raw("Failed to delete keystore", e));
403        }
404      }
405
406      File keystorePin = new File(installation.getConfigurationDirectory(),
407              "keystore.pin");
408      if (keystorePin.exists()) {
409        try {
410          fm.delete(keystorePin);
411        } catch (ApplicationException e) {
412          logger.info(LocalizableMessage.raw("Failed to delete keystore.pin", e));
413        }
414      }
415
416      File truststore = new File(installation.getConfigurationDirectory(),
417              "truststore");
418      if (truststore.exists()) {
419        try {
420          fm.delete(truststore);
421        } catch (ApplicationException e) {
422          logger.info(LocalizableMessage.raw("Failed to delete truststore", e));
423        }
424      }
425    }
426
427    // Remove the databases
428    try {
429      fm.deleteChildren(installation.getDatabasesDirectory());
430    } catch (ApplicationException e) {
431      logger.info(LocalizableMessage.raw("Error deleting databases", e));
432    }
433
434    if (!isVerbose())
435    {
436      notifyListeners(getFormattedDoneWithLineBreak());
437    }
438
439  }
440
441  /**
442   * Initialize the different map used in this class.
443   *
444   */
445  protected void initMaps()
446  {
447    initSummaryMap(hmSummary, true);
448
449    /*
450     * hmTime contains the relative time that takes for each task to be
451     * accomplished. For instance if downloading takes twice the time of
452     * extracting, the value for downloading will be the double of the value for
453     * extracting.
454     */
455    Map<ProgressStep, Integer> hmTime = new HashMap<>();
456    hmTime.put(InstallProgressStep.CONFIGURING_SERVER, 5);
457    hmTime.put(InstallProgressStep.CREATING_BASE_ENTRY, 10);
458    hmTime.put(InstallProgressStep.IMPORTING_LDIF, 20);
459    hmTime.put(InstallProgressStep.IMPORTING_AUTOMATICALLY_GENERATED, 20);
460    hmTime.put(InstallProgressStep.CONFIGURING_REPLICATION, 10);
461    hmTime.put(InstallProgressStep.ENABLING_WINDOWS_SERVICE, 5);
462    hmTime.put(InstallProgressStep.STARTING_SERVER, 10);
463    hmTime.put(InstallProgressStep.STOPPING_SERVER, 5);
464    hmTime.put(InstallProgressStep.CONFIGURING_ADS, 5);
465    hmTime.put(InstallProgressStep.INITIALIZE_REPLICATED_SUFFIXES, 25);
466
467    int totalTime = 0;
468    List<InstallProgressStep> steps = new ArrayList<>();
469    totalTime += hmTime.get(InstallProgressStep.CONFIGURING_SERVER);
470    steps.add(InstallProgressStep.CONFIGURING_SERVER);
471    if (createNotReplicatedSuffix())
472    {
473      switch (getUserData().getNewSuffixOptions().getType())
474      {
475      case CREATE_BASE_ENTRY:
476        steps.add(InstallProgressStep.CREATING_BASE_ENTRY);
477        totalTime += hmTime.get(InstallProgressStep.CREATING_BASE_ENTRY);
478        break;
479      case IMPORT_FROM_LDIF_FILE:
480        steps.add(InstallProgressStep.IMPORTING_LDIF);
481        totalTime += hmTime.get(InstallProgressStep.IMPORTING_LDIF);
482        break;
483      case IMPORT_AUTOMATICALLY_GENERATED_DATA:
484        steps.add(InstallProgressStep.IMPORTING_AUTOMATICALLY_GENERATED);
485        totalTime += hmTime.get(
486            InstallProgressStep.IMPORTING_AUTOMATICALLY_GENERATED);
487        break;
488      }
489    }
490
491    if (isWindows() && getUserData().getEnableWindowsService())
492    {
493      totalTime += hmTime.get(InstallProgressStep.ENABLING_WINDOWS_SERVICE);
494      steps.add(InstallProgressStep.ENABLING_WINDOWS_SERVICE);
495    }
496
497    if (mustStart())
498    {
499      totalTime += hmTime.get(InstallProgressStep.STARTING_SERVER);
500      steps.add(InstallProgressStep.STARTING_SERVER);
501    }
502
503    if (mustCreateAds())
504    {
505      totalTime += hmTime.get(InstallProgressStep.CONFIGURING_ADS);
506      steps.add(InstallProgressStep.CONFIGURING_ADS);
507    }
508
509    if (mustConfigureReplication())
510    {
511      steps.add(InstallProgressStep.CONFIGURING_REPLICATION);
512      totalTime += hmTime.get(InstallProgressStep.CONFIGURING_REPLICATION);
513    }
514
515    if (mustInitializeSuffixes())
516    {
517      totalTime += hmTime.get(
518          InstallProgressStep.INITIALIZE_REPLICATED_SUFFIXES);
519      steps.add(InstallProgressStep.INITIALIZE_REPLICATED_SUFFIXES);
520    }
521
522    if (mustStop())
523    {
524      totalTime += hmTime.get(InstallProgressStep.STOPPING_SERVER);
525      steps.add(InstallProgressStep.STOPPING_SERVER);
526    }
527
528    int cumulatedTime = 0;
529    for (InstallProgressStep s : steps)
530    {
531      Integer statusTime = hmTime.get(s);
532      hmRatio.put(s, (100 * cumulatedTime) / totalTime);
533      if (statusTime != null)
534      {
535        cumulatedTime += statusTime;
536      }
537    }
538    hmRatio.put(InstallProgressStep.FINISHED_SUCCESSFULLY, 100);
539    hmRatio.put(InstallProgressStep.FINISHED_WITH_ERROR, 100);
540    hmRatio.put(InstallProgressStep.FINISHED_CANCELED, 100);
541  }
542
543  /** {@inheritDoc} */
544  @Override
545  public String getInstallationPath()
546  {
547    return Utils.getInstallPathFromClasspath();
548  }
549
550  /** {@inheritDoc} */
551  @Override
552  public String getInstancePath()
553  {
554    String installPath =  Utils.getInstallPathFromClasspath();
555    return Utils.getInstancePathFromInstallPath(installPath);
556  }
557
558}