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