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-2016 ForgeRock AS.
016 */
017package org.opends.quicksetup;
018
019import static com.forgerock.opendj.util.OperatingSystem.*;
020
021import static org.opends.messages.QuickSetupMessages.*;
022
023import java.io.BufferedReader;
024import java.io.File;
025import java.io.FileReader;
026import java.io.IOException;
027import java.util.Set;
028import java.util.concurrent.Callable;
029import java.util.concurrent.ExecutionException;
030import java.util.concurrent.FutureTask;
031
032import org.forgerock.i18n.LocalizableMessage;
033import org.forgerock.i18n.slf4j.LocalizedLogger;
034import org.opends.quicksetup.util.Utils;
035import org.opends.server.util.CollectionUtils;
036import org.opends.server.util.SetupUtils;
037
038/**
039 * This class represents the physical state of an OpenDJ installation. All the
040 * operations are dependent upon the root directory that is specified in the
041 * constructor.
042 */
043public final class Installation
044{
045  /** Relative path to bootstrap OpenDJ jar file. */
046  public static final String OPENDJ_BOOTSTRAP_JAR_RELATIVE_PATH = "lib/bootstrap.jar";
047  /** Relative path to bootstrap-client OpenDJ jar file. */
048  public static final String OPENDJ_BOOTSTRAP_CLIENT_JAR_RELATIVE_PATH = "lib/bootstrap-client.jar";
049
050  /** The relative path where all the Windows binaries (batch files) are. */
051  public static final String WINDOWS_BINARIES_PATH_RELATIVE = "bat";
052  /** The relative path where all the UNIX binaries (scripts) are. */
053  public static final String UNIX_BINARIES_PATH_RELATIVE = "bin";
054  /** The relative path where all the MacOS X Applications are. */
055  public static final String MAC_APPLICATIONS_PATH_RELATIVE = "bin";
056  /** The relative path where all the libraries (jar files) are. */
057  public static final String LIBRARIES_PATH_RELATIVE = SetupUtils.LIBRARIES_PATH_RELATIVE;
058  /** The relative path where the resources directory (to customize the product) is. */
059  public static final String RESOURCES_PATH_RELATIVE = "resources";
060  /** The relative path where customer classes are. */
061  public static final String CLASSES_PATH_RELATIVE = "classes";
062  /** The relative path where the database files are. */
063  public static final String DATABASES_PATH_RELATIVE = "db";
064  /** The relative path where the log files are. */
065  public static final String LOGS_PATH_RELATIVE = "logs";
066  /** The relative path where the LDIF files are. */
067  public static final String LDIFS_PATH_RELATIVE = "ldif";
068  /** The relative path where the backup files are. */
069  public static final String BACKUPS_PATH_RELATIVE = "bak";
070  /** The relative path where the config files are. */
071  public static final String CONFIG_PATH_RELATIVE = "config";
072  /** The relative path where the config files are. */
073  public static final String HISTORY_PATH_RELATIVE = "history";
074  /** Path to the config/upgrade directory where upgrade base files are stored. */
075  public static final String UPGRADE_PATH = "upgrade";
076  /** Relative path to the locks directory. */
077  public static final String LOCKS_PATH_RELATIVE = "locks";
078  /** Relative path to the locks directory. */
079  public static final String TMP_PATH_RELATIVE = "tmp";
080  /** The relative path to the current Configuration LDIF file. */
081  public static final String CURRENT_CONFIG_FILE_NAME = "config.ldif";
082  /** The relative path to the current Configuration LDIF file. */
083  public static final String BASE_CONFIG_FILE_PREFIX = "config.ldif.";
084  /** The relative path to the instance.loc file. */
085  public static final String INSTANCE_LOCATION_PATH_RELATIVE = "instance.loc";
086  /** The path to the instance.loc file. */
087  public static final String INSTANCE_LOCATION_PATH = "/etc/opendj/"
088      + INSTANCE_LOCATION_PATH_RELATIVE;
089  /** The relative path to tmpl_instance. */
090  public static final String TEMPLATE_RELATIVE_PATH = "template";
091  /** The relative path to buildinfo file. */
092  public static final String BUILDINFO_RELATIVE_PATH = "buildinfo";
093  /** The UNIX setup script file name. */
094  public static final String UNIX_SETUP_FILE_NAME = "setup";
095  /** The Windows setup batch file name. */
096  public static final String WINDOWS_SETUP_FILE_NAME = "setup.bat";
097  /** The UNIX uninstall script file name. */
098  public static final String UNIX_UNINSTALL_FILE_NAME = "uninstall";
099  /** The Windows uninstall batch file name. */
100  public static final String WINDOWS_UNINSTALL_FILE_NAME = "uninstall.bat";
101  /** The UNIX upgrade script file name. */
102  public static final String UNIX_UPGRADE_FILE_NAME = "upgrade";
103  /** The UNIX start script file name. */
104  public static final String UNIX_START_FILE_NAME = "start-ds";
105  /** The Windows start batch file name. */
106  public static final String WINDOWS_START_FILE_NAME = "start-ds.bat";
107  /** The UNIX stop script file name. */
108  public static final String UNIX_STOP_FILE_NAME = "stop-ds";
109  /** The Windows stop batch file name. */
110  public static final String WINDOWS_STOP_FILE_NAME = "stop-ds.bat";
111  /** The UNIX control panel script file name. */
112  public static final String UNIX_CONTROLPANEL_FILE_NAME = "control-panel";
113  /** The Windows control panel batch file name. */
114  public static final String WINDOWS_CONTROLPANEL_FILE_NAME = "control-panel.bat";
115  /** The MacOS X Java application stub name. */
116  public static final String MAC_JAVA_APP_STUB_NAME = "universalJavaApplicationStub";
117  /** The MacOS X control panel application bundle name. */
118  public static final String MAC_CONTROLPANEL_FILE_NAME = "ControlPanel.app";
119  /** The UNIX status command line script file name. */
120  public static final String UNIX_STATUSCLI_FILE_NAME = "status";
121  /** The Windows status command line batch file name. */
122  public static final String WINDOWS_STATUSCLI_FILE_NAME = "status.bat";
123  /** The UNIX import LDIF script file name. */
124  public static final String UNIX_IMPORT_LDIF = "import-ldif";
125  /** The Windows import LDIF batch file name. */
126  public static final String WINDOWS_IMPORT_LDIF = "import-ldif.bat";
127
128  /** Name of the file kept in the history directory containing logs of upgrade and reversions. */
129  public static final String HISTORY_LOG_FILE_NAME = "log";
130  /** The default java properties file. */
131  public static final String DEFAULT_JAVA_PROPERTIES_FILE = "java.properties";
132  /** The default java properties file relative path. */
133  public static final String RELATIVE_JAVA_PROPERTIES_FILE =
134      CONFIG_PATH_RELATIVE + File.separator + "java.properties";
135  /** The set java home and arguments properties file for Windows. */
136  public static final String SET_JAVA_PROPERTIES_FILE_WINDOWS = "set-java-home.bat";
137  /** Script utils file for UNIX systems. */
138  public static final String SCRIPT_UTIL_FILE_UNIX = "_script-util.sh";
139  /** Script utils file for Windows. */
140  public static final String SCRIPT_UTIL_FILE_WINDOWS = "_script-util.bat";
141  /** The set java home and arguments properties file for UNIX systems. */
142  public static final String SET_JAVA_PROPERTIES_FILE_UNIX = "set-java-home";
143
144  /** Directories required to be present for this installation to be considered valid. */
145  public static final String[] REQUIRED_DIRECTORIES = new String[] {
146      CONFIG_PATH_RELATIVE, DATABASES_PATH_RELATIVE, LIBRARIES_PATH_RELATIVE };
147
148  /** The default base DN prompted to user in setup interactive mode. */
149  public static final String DEFAULT_INTERACTIVE_BASE_DN = "dc=example,dc=com";
150
151  /**
152   * Performs validation on the specified file to make sure that it is an actual
153   * OpenDJ installation.
154   *
155   * @param rootDirectory
156   *          File directory candidate
157   * @throws IllegalArgumentException
158   *           if root directory does not appear to be an OpenDJ installation
159   *           root. The thrown exception contains a localized message
160   *           indicating the reason why <code>rootDirectory</code> is not a
161   *           valid OpenDJ install root.
162   */
163  public static void validateRootDirectory(File rootDirectory)
164      throws IllegalArgumentException
165  {
166    LocalizableMessage failureReason = null;
167    if (rootDirectory == null)
168    {
169      failureReason = INFO_ERROR_INSTALL_ROOT_DIR_NULL.get();
170    }
171    else if (!rootDirectory.exists())
172    {
173      failureReason = INFO_ERROR_INSTALL_ROOT_DIR_NO_EXIST.get(Utils
174          .getPath(rootDirectory));
175    }
176    else if (!rootDirectory.isDirectory())
177    {
178      failureReason = INFO_ERROR_INSTALL_ROOT_DIR_NOT_DIR.get(Utils
179          .getPath(rootDirectory));
180    }
181    else
182    {
183      String[] children = rootDirectory.list();
184      if (children != null)
185      {
186        Set<String> childrenSet = CollectionUtils.newHashSet(children);
187        for (String dir : REQUIRED_DIRECTORIES)
188        {
189          if (!childrenSet.contains(dir))
190          {
191            failureReason = INFO_ERROR_INSTALL_ROOT_DIR_NO_DIR.get(
192                Utils.getPath(rootDirectory), dir);
193          }
194        }
195      }
196      else
197      {
198        failureReason = INFO_ERROR_INSTALL_ROOT_DIR_EMPTY.get(Utils
199            .getPath(rootDirectory));
200      }
201    }
202    if (failureReason != null)
203    {
204      throw new IllegalArgumentException(failureReason.toString());
205    }
206  }
207
208  private static Installation local;
209
210  /**
211   * Obtains the installation by reading the classpath of the running JVM to
212   * determine the location of the jars and determine the installation root.
213   *
214   * @return Installation obtained by reading the classpath
215   */
216  public static Installation getLocal()
217  {
218    if (local == null)
219    {
220      // This allows testing of configuration components when the OpenDJ.jar
221      // in the classpath does not necessarily point to the server's
222      String installRoot = System.getProperty("org.opends.quicksetup.Root");
223      String instanceRoot = System
224          .getProperty("org.opends.quicksetup.instance");
225
226      if (installRoot == null)
227      {
228        installRoot = Utils.getInstallPathFromClasspath();
229      }
230      if (instanceRoot == null)
231      {
232        instanceRoot = Utils.getInstancePathFromInstallPath(installRoot);
233      }
234      local = new Installation(installRoot, instanceRoot);
235    }
236    return local;
237  }
238
239  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
240
241  private File rootDirectory;
242  private File instanceDirectory;
243
244  private Status status;
245
246  private Configuration configuration;
247  private Configuration baseConfiguration;
248
249  private BuildInformation buildInformation;
250  private BuildInformation instanceInformation;
251
252  /**
253   * Creates a new instance from a root directory specified as a string.
254   *
255   * @param rootDirectory
256   *          of this installation
257   * @param instanceRootDirectory
258   *          The instance root directory
259   */
260  public Installation(String rootDirectory, String instanceRootDirectory)
261  {
262    this(new File(rootDirectory), new File(instanceRootDirectory));
263  }
264
265  /**
266   * Creates a new instance from a root directory specified as a File.
267   *
268   * @param rootDirectory
269   *          of this installation
270   * @param instanceDirectory
271   *          of the instance
272   */
273  public Installation(File rootDirectory, File instanceDirectory)
274  {
275    setRootDirectory(rootDirectory);
276    setInstanceDirectory(instanceDirectory);
277  }
278
279  /**
280   * Gets the top level directory of an OpenDJ installation.
281   *
282   * @return File object representing the top level directory of and OpenDJ
283   *         installation
284   */
285  public File getRootDirectory()
286  {
287    return this.rootDirectory;
288  }
289
290  /**
291   * Gets the top level directory of an OpenDJ instance.
292   *
293   * @return File object representing the top level directory of and OpenDK
294   *         installation
295   */
296  public File getInstanceDirectory()
297  {
298    return this.instanceDirectory;
299  }
300
301  /**
302   * Sets the root directory of this installation.
303   *
304   * @param rootDirectory
305   *          File of this installation
306   */
307  public void setRootDirectory(File rootDirectory)
308  {
309    // Hold off on doing validation of rootDirectory since
310    // some applications (like the Installer) create an Installation
311    // before the actual bits have been laid down on the file system.
312    this.rootDirectory = rootDirectory;
313
314    // Obtaining build information is a fairly time consuming operation.
315    // Try to get a head start if possible.
316    if (isValid(rootDirectory))
317    {
318      try
319      {
320        BuildInformation bi = getBuildInformation();
321        logger.info(LocalizableMessage.raw("build info for " + rootDirectory.getName() + ": "
322            + bi));
323      }
324      catch (ApplicationException e)
325      {
326        logger.info(LocalizableMessage.raw("error determining build information", e));
327      }
328    }
329  }
330
331  /**
332   * Sets the root directory of this instance.
333   *
334   * @param instanceDirectory
335   *          File of this instance
336   */
337  public void setInstanceDirectory(File instanceDirectory)
338  {
339    // Hold off on doing validation of rootDirectory since
340    // some applications (like the Installer) create an Installation
341    // before the actual bits have been laid down on the filesystem.
342    this.instanceDirectory = instanceDirectory;
343
344    // Obtaining build information is a fairly time consuming operation.
345    // Try to get a head start if possible.
346    if (isValid(instanceDirectory))
347    {
348      try
349      {
350        BuildInformation bi = getBuildInformation();
351        logger.info(LocalizableMessage.raw("build info for " + instanceDirectory.getName()
352            + ": " + bi));
353      }
354      catch (ApplicationException e)
355      {
356        logger.info(LocalizableMessage.raw("error determining build information", e));
357      }
358    }
359  }
360
361  /**
362   * Indicates whether or not this installation appears to be an actual OpenDJ
363   * installation.
364   *
365   * @param file
366   *          The root directory
367   * @return boolean where true indicates that this does indeed appear to be a
368   *         valid OpenDJ installation; false otherwise
369   */
370  public boolean isValid(File file)
371  {
372    try
373    {
374      validateRootDirectory(file);
375      return true;
376    }
377    catch (IllegalArgumentException e)
378    {
379      return false;
380    }
381  }
382
383  /**
384   * Creates a string explaining why this is not a legitimate OpenDJ
385   * installation. Null if this is in fact a valid installation.
386   *
387   * @return localized message indicating the reason this is not an OpenDJ
388   *         installation
389   */
390  public String getInvalidityReason()
391  {
392    try
393    {
394      validateRootDirectory(rootDirectory);
395      return null;
396    }
397    catch (IllegalArgumentException e)
398    {
399      return e.getLocalizedMessage();
400    }
401  }
402
403  /**
404   * Gets the Configuration object representing this file. The current
405   * configuration is stored in config/config.ldif.
406   *
407   * @return Configuration representing the current configuration.
408   */
409  public Configuration getCurrentConfiguration()
410  {
411    if (configuration == null)
412    {
413      configuration = new Configuration(this, getCurrentConfigurationFile());
414    }
415    return configuration;
416  }
417
418  /**
419   * Gets the Configuration object representing this file. The base
420   * configuration is stored in config/upgrade/config.ldif.[svn rev].
421   *
422   * @return Configuration object representing the base configuration.
423   * @throws ApplicationException
424   *           if there was a problem determining the svn rev number.
425   */
426  public Configuration getBaseConfiguration() throws ApplicationException
427  {
428    if (baseConfiguration == null)
429    {
430      baseConfiguration = new Configuration(this, getBaseConfigurationFile());
431    }
432    return baseConfiguration;
433  }
434
435  /**
436   * Gets the current status of this installation.
437   *
438   * @return Status object representing the state of this installation.
439   */
440  public Status getStatus()
441  {
442    if (status == null)
443    {
444      status = new Status(this);
445    }
446    return status;
447  }
448
449  /**
450   * Returns the path to the libraries.
451   *
452   * @return the path to the libraries.
453   */
454  public File getLibrariesDirectory()
455  {
456    return new File(getRootDirectory(), LIBRARIES_PATH_RELATIVE);
457  }
458
459  /**
460   * Returns the path to the resources directory.
461   *
462   * @return the path to the resources directory.
463   */
464  public File getResourcesDirectory()
465  {
466    return new File(getRootDirectory(), RESOURCES_PATH_RELATIVE);
467  }
468
469  /**
470   * Returns the path to the classes directory.
471   *
472   * @return the path to the classes directory.
473   */
474  public File getClassesDirectory()
475  {
476    return new File(getRootDirectory(), CLASSES_PATH_RELATIVE);
477  }
478
479  /**
480   * Creates a File object representing config/upgrade/schema.ldif.current which
481   * the server creates the first time it starts if there are schema
482   * customizations.
483   *
484   * @return File object with no
485   */
486  public File getSchemaConcatFile()
487  {
488    return new File(getConfigurationUpgradeDirectory(), "schema.ldif.current");
489  }
490
491  /**
492   * Creates a File object representing config/upgrade/schema.ldif.current which
493   * the server creates the first time it starts if there are schema
494   * customizations.
495   *
496   * @return File object with no
497   * @throws ApplicationException
498   *           if there was a problem determining the svn revision number
499   */
500  public File getBaseSchemaFile() throws ApplicationException
501  {
502    return new File(getConfigurationUpgradeDirectory(), "schema.ldif." + getInstanceVCSRevision());
503  }
504
505  /**
506   * Creates a File object representing config/upgrade/schema.ldif.current which
507   * the server creates the first time it starts if there are schema
508   * customizations.
509   *
510   * @return File object with no
511   * @throws ApplicationException
512   *           if there was a problem determining the svn revision number
513   */
514  public File getBaseConfigurationFile() throws ApplicationException
515  {
516    return new File(getConfigurationUpgradeDirectory(), BASE_CONFIG_FILE_PREFIX + getInstanceVCSRevision());
517  }
518
519  /**
520   * Gets the VCS revision of the build.
521   *
522   * @return String representing the VCS revision
523   * @throws ApplicationException
524   *           if for some reason the number could not be determined
525   */
526  public String getVCSRevision() throws ApplicationException
527  {
528    return getBuildInformation().getRevision();
529  }
530
531  /**
532   * Gets the VCS revision of the instance.
533   *
534   * @return Integer representing the svn number
535   * @throws ApplicationException
536   *           if for some reason the number could not be determined
537   */
538  public String getInstanceVCSRevision() throws ApplicationException
539  {
540    return getInstanceBuildInformation().getRevision();
541  }
542
543  /**
544   * Returns the path to the configuration file of the directory server. Note
545   * that this method assumes that this code is being run locally.
546   *
547   * @return the path of the configuration file of the directory server.
548   */
549  public File getCurrentConfigurationFile()
550  {
551    return new File(getConfigurationDirectory(), CURRENT_CONFIG_FILE_NAME);
552  }
553
554  /**
555   * Returns the relative path of the directory containing the binaries/scripts
556   * of the Open DS installation. The path is relative to the installation path.
557   *
558   * @return the relative path of the directory containing the binaries/scripts
559   *         of the Open DS installation.
560   */
561  public File getBinariesDirectory()
562  {
563    String binDir = isWindows() ? WINDOWS_BINARIES_PATH_RELATIVE : UNIX_BINARIES_PATH_RELATIVE;
564    return new File(getRootDirectory(), binDir);
565  }
566
567  /**
568   * Returns the path to the database files under the install path.
569   *
570   * @return the path to the database files under the install path.
571   */
572  public File getDatabasesDirectory()
573  {
574    return new File(getInstanceDirectory(), DATABASES_PATH_RELATIVE);
575  }
576
577  /**
578   * Returns the path to the backup files under the install path.
579   *
580   * @return the path to the backup files under the install path.
581   */
582  public File getBackupDirectory()
583  {
584    return new File(getInstanceDirectory(), BACKUPS_PATH_RELATIVE);
585  }
586
587  /**
588   * Returns the path to the config files under the install path.
589   *
590   * @return the path to the config files under the install path.
591   */
592  public File getConfigurationDirectory()
593  {
594    return new File(getInstanceDirectory(), CONFIG_PATH_RELATIVE);
595  }
596
597  /**
598   * Returns the path to the log files under the install path.
599   *
600   * @return the path to the log files under the install path.
601   */
602  public File getLogsDirectory()
603  {
604    return new File(getInstanceDirectory(), LOGS_PATH_RELATIVE);
605  }
606
607  /**
608   * Returns the directory where the lock files are stored.
609   *
610   * @return the path to the lock files.
611   */
612  public File getLocksDirectory()
613  {
614    return new File(getInstanceDirectory(), LOCKS_PATH_RELATIVE);
615  }
616
617  /**
618   * Gets the directory used to store the template configuration.
619   *
620   * @return The directory used to store the template configuration.
621   */
622  public File getTemplateDirectory()
623  {
624    return new File(getRootDirectory(), TEMPLATE_RELATIVE_PATH);
625  }
626
627  /**
628   * Gets the directory used to store files temporarily.
629   *
630   * @return File temporary directory
631   */
632  public File getTemporaryDirectory()
633  {
634    return new File(getInstanceDirectory(), TMP_PATH_RELATIVE);
635  }
636
637  /**
638   * Returns the directory where the lock files are stored.
639   *
640   * @return the path to the lock files.
641   */
642  public File getHistoryDirectory()
643  {
644    return new File(getInstanceDirectory(), HISTORY_PATH_RELATIVE);
645  }
646
647  /**
648   * Creates a new directory in the history directory appropriate for backing up
649   * an installation during an upgrade.
650   *
651   * @return File representing a new backup directory. The directory can be
652   *         assumed to exist if this method returns cleanly.
653   * @throws IOException
654   *           if an error occurred creating the directory.
655   */
656  public File createHistoryBackupDirectory() throws IOException
657  {
658    File backupDirectory = new File(getHistoryDirectory(), Long.toString(System
659        .currentTimeMillis()));
660    if (backupDirectory.exists())
661    {
662      backupDirectory.delete();
663    }
664    if (!backupDirectory.mkdirs())
665    {
666      throw new IOException("failed to create history backup directory");
667    }
668    return backupDirectory;
669  }
670
671  /**
672   * Gets the log file where the history of upgrades and reversions is kept.
673   *
674   * @return File containing upgrade/reversion history.
675   */
676  public File getHistoryLogFile()
677  {
678    return new File(getHistoryDirectory(), HISTORY_LOG_FILE_NAME);
679  }
680
681  /**
682   * Gets the directory config/upgrade.
683   *
684   * @return File representing the config/upgrade directory
685   */
686  public File getConfigurationUpgradeDirectory()
687  {
688    return new File(getConfigurationDirectory(), UPGRADE_PATH);
689  }
690
691  /**
692   * Gets the directory where the upgrader stores files temporarily.
693   *
694   * @return File representing the upgrader's temporary directory
695   */
696  public File getTemporaryUpgradeDirectory()
697  {
698    return new File(getTemporaryDirectory(), UPGRADE_PATH);
699  }
700
701  /**
702   * Gets the file for invoking a particular command appropriate for the current
703   * operating system.
704   *
705   * @param command
706   *          name of the command
707   * @return File representing the command
708   */
709  public File getCommandFile(String command)
710  {
711    String filename = isWindows() ? command + ".bat" : command;
712    return new File(getBinariesDirectory(), filename);
713  }
714
715  /**
716   * Gets the file responsible for stopping the server appropriate for the
717   * current operating system.
718   *
719   * @return File representing the stop command
720   */
721  public File getServerStartCommandFile()
722  {
723    return getCommandFile(UNIX_START_FILE_NAME);
724  }
725
726  /**
727   * Gets the file responsible for stopping the server appropriate for the
728   * current operating system.
729   *
730   * @return File representing the stop command
731   */
732  public File getServerStopCommandFile()
733  {
734    return getCommandFile(UNIX_STOP_FILE_NAME);
735  }
736
737  /**
738   * Returns the setup file name to use with the current operating system.
739   *
740   * @return the setup file name to use with the current operating system.
741   */
742  public static String getSetupFileName()
743  {
744    return isWindows() ? WINDOWS_SETUP_FILE_NAME : UNIX_SETUP_FILE_NAME;
745  }
746
747  /**
748   * Returns the 'ldif' directory.
749   *
750   * @return the 'ldif' directory.
751   */
752  public File getLdifDirectory()
753  {
754    return new File(getRootDirectory(), LDIFS_PATH_RELATIVE);
755  }
756
757  /**
758   * Returns the path to the quicksetup jar file.
759   *
760   * @return the path to the quicksetup jar file.
761   */
762  public File getQuicksetupJarFile()
763  {
764    return new File(getLibrariesDirectory(), "quicksetup.jar");
765  }
766
767  /**
768   * Returns the path to the opends jar file.
769   *
770   * @return the path to the opends jar file.
771   */
772  public File getOpenDSJarFile()
773  {
774    return new File(getLibrariesDirectory(), "OpenDJ.jar");
775  }
776
777  /**
778   * Returns the path to the uninstall.bat file.
779   *
780   * @return the path to the uninstall.bat file.
781   */
782  public File getUninstallBatFile()
783  {
784    return new File(getRootDirectory(), "uninstall.bat");
785  }
786
787  /**
788   * Gets the control panel command file appropriate for the current operating
789   * system.
790   *
791   * @return File object representing the control panel command
792   */
793  public File getControlPanelCommandFile()
794  {
795    if (isMacOS())
796    {
797      String binDir = getRootDirectory() + File.separator + MAC_APPLICATIONS_PATH_RELATIVE;
798      return new File(binDir, MAC_CONTROLPANEL_FILE_NAME);
799    }
800    return getCommandFile(UNIX_CONTROLPANEL_FILE_NAME);
801  }
802
803   /**
804   * Gets information about the build that was used to produce the bits for this
805   * installation.
806   *
807   * @return BuildInformation object describing this installation
808   * @throws ApplicationException
809   *           if there is a problem obtaining the build information
810   */
811  public BuildInformation getBuildInformation() throws ApplicationException
812  {
813    return getBuildInformation(true);
814  }
815
816  /**
817   * Gets information about the build that was used to produce the bits for this
818   * installation.
819   *
820   * @param useCachedVersion
821   *          where true indicates that a potentially cached version of the
822   *          build information is acceptable for use; false indicates the the
823   *          build information will be created from scratch which is
824   *          potentially time consuming
825   * @return BuildInformation object describing this installation
826   * @throws ApplicationException
827   *           if there is a problem obtaining the build information
828   */
829  public BuildInformation getBuildInformation(boolean useCachedVersion)
830      throws ApplicationException
831  {
832    if (buildInformation == null || !useCachedVersion)
833    {
834      FutureTask<BuildInformation> ft = new FutureTask<>(
835          new Callable<BuildInformation>()
836          {
837            @Override
838            public BuildInformation call() throws ApplicationException
839            {
840              return BuildInformation.create(Installation.this);
841            }
842          });
843      new Thread(ft).start();
844      try
845      {
846        buildInformation = ft.get();
847      }
848      catch (InterruptedException e)
849      {
850        logger.info(LocalizableMessage.raw("interrupted trying to get build information", e));
851      }
852      catch (ExecutionException e)
853      {
854        throw (ApplicationException) e.getCause();
855      }
856    }
857    return buildInformation;
858  }
859
860  /**
861   * Gets information about the build that was used to produce the instance.
862   *
863   * @return BuildInformation object describing this instance
864   */
865  public BuildInformation getInstanceBuildInformation()
866  {
867    return getInstanceBuildInformation(true);
868  }
869
870  /**
871   * Gets information about the build that was used to produce the instance.
872   *
873   * @param useCachedVersion
874   *          where true indicates that a potentially cached version of the
875   *          build information is acceptable for use; false indicates the build
876   *          information will be created from scratch which is potentially time
877   *          consuming
878   * @return BuildInformation object describing this instance
879   */
880  public BuildInformation getInstanceBuildInformation(boolean useCachedVersion)
881  {
882    if (instanceInformation == null || !useCachedVersion)
883    {
884      try
885      {
886        File bif = new File(getConfigurationDirectory(), BUILDINFO_RELATIVE_PATH);
887        if (bif.exists())
888        {
889          // Read the first line and close the file.
890          try (BufferedReader reader = new BufferedReader(new FileReader(bif)))
891          {
892            instanceInformation = BuildInformation.fromBuildString(reader.readLine());
893          }
894        }
895        else
896        {
897          return getBuildInformation();
898        }
899      }
900      catch (Exception e)
901      {
902        logger.error(LocalizableMessage.raw("error getting build information for current instance", e));
903      }
904    }
905    return instanceInformation;
906  }
907}