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
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027
028 package org.opends.server.tools;
029
030 import static org.opends.messages.AdminToolMessages.*;
031 import static org.opends.messages.QuickSetupMessages.*;
032 import static org.opends.messages.ToolMessages.*;
033 import static org.opends.messages.UtilityMessages.*;
034
035 import java.io.File;
036 import java.io.InputStream;
037 import java.io.OutputStream;
038 import java.io.PrintStream;
039 import java.security.KeyStoreException;
040 import java.util.Collection;
041 import java.util.LinkedList;
042 import java.util.logging.Level;
043 import java.util.logging.Logger;
044
045 import org.opends.messages.Message;
046 import org.opends.messages.ToolMessages;
047 import org.opends.quicksetup.ApplicationException;
048 import org.opends.quicksetup.Constants;
049 import org.opends.quicksetup.CurrentInstallStatus;
050 import org.opends.quicksetup.Installation;
051 import org.opends.quicksetup.QuickSetupLog;
052 import org.opends.quicksetup.ReturnCode;
053 import org.opends.quicksetup.SecurityOptions;
054 import org.opends.quicksetup.UserData;
055 import org.opends.quicksetup.UserDataException;
056 import org.opends.quicksetup.event.ProgressUpdateEvent;
057 import org.opends.quicksetup.event.ProgressUpdateListener;
058 import org.opends.quicksetup.installer.NewSuffixOptions;
059 import org.opends.quicksetup.installer.offline.OfflineInstaller;
060 import org.opends.quicksetup.installer.ui.InstallReviewPanel;
061 import org.opends.quicksetup.ui.QuickSetupStepPanel;
062 import org.opends.quicksetup.util.IncompatibleVersionException;
063 import org.opends.quicksetup.util.PlainTextProgressMessageFormatter;
064 import org.opends.quicksetup.util.Utils;
065 import org.opends.server.core.DirectoryServer;
066 import org.opends.server.types.DN;
067 import org.opends.server.types.InitializationException;
068 import org.opends.server.types.NullOutputStream;
069 import org.opends.server.util.CertificateManager;
070 import org.opends.server.util.SetupUtils;
071 import org.opends.server.util.args.ArgumentException;
072 import org.opends.server.util.args.IntegerArgument;
073 import org.opends.server.util.args.StringArgument;
074 import org.opends.server.util.cli.CLIException;
075 import org.opends.server.util.cli.ConsoleApplication;
076 import org.opends.server.util.cli.Menu;
077 import org.opends.server.util.cli.MenuBuilder;
078 import org.opends.server.util.cli.MenuResult;
079 /**
080 * This class provides a very simple mechanism for installing the OpenDS
081 * Directory Service. It performs the following tasks:
082 * <UL>
083 * <LI>Checks if the server is already installed and running</LI>
084 * <LI>Ask the user what base DN should be used for the data</LI>
085 * <LI>Ask the user whether to create the base entry, or to import LDIF</LI>
086 * <LI>Ask the user for the LDAP port and make sure it's available</LI>
087 * <LI>Ask the user for the default root DN and password</LI>
088 * <LI>Ask the user to enable SSL or not and for the type of certificate that
089 * the server must use</LI>
090 * <LI>Ask the user if they want to start the server when done installing</LI>
091 * </UL>
092 */
093 public class InstallDS extends ConsoleApplication
094 {
095 private PlainTextProgressMessageFormatter formatter =
096 new PlainTextProgressMessageFormatter();
097 /** Prefix for log files. */
098 static public final String LOG_FILE_PREFIX = "opends-setup-";
099
100 /** Suffix for log files. */
101 static public final String LOG_FILE_SUFFIX = ".log";
102
103 /**
104 * The enumeration containing the different return codes that the command-line
105 * can have.
106 *
107 */
108 enum ErrorReturnCode
109 {
110 /**
111 * Successful setup.
112 */
113 SUCCESSFUL(0),
114 /**
115 * We did no have an error but the setup was not executed (displayed
116 * version or usage).
117 */
118 SUCCESSFUL_NOP(0),
119 /**
120 * Unexpected error (potential bug).
121 */
122 ERROR_UNEXPECTED(1),
123 /**
124 * Cannot parse arguments or data provided by user is not valid.
125 */
126 ERROR_USER_DATA(2),
127 /**
128 * Error server already installed.
129 */
130 ERROR_SERVER_ALREADY_INSTALLED(3),
131 /**
132 * Error initializing server.
133 */
134 ERROR_INITIALIZING_SERVER(4),
135 /**
136 * The user failed providing password (for the keystore for instance).
137 */
138 ERROR_PASSWORD_LIMIT(5),
139 /**
140 * The user cancelled the setup.
141 */
142 ERROR_USER_CANCELLED(6);
143
144 private int returnCode;
145 private ErrorReturnCode(int returnCode)
146 {
147 this.returnCode = returnCode;
148 }
149
150 /**
151 * Get the corresponding return code value.
152 *
153 * @return The corresponding return code value.
154 */
155 public int getReturnCode()
156 {
157 return returnCode;
158 }
159 };
160
161 /**
162 * Enumeration describing the different answer that the user can provide
163 * when we ask to finalize the setup. Note that the code associated
164 * correspond to the order in the confirmation menu that is displayed at the
165 * end of the setup in interactive mode.
166 */
167 private enum ConfirmCode
168 {
169 // Continue with the install
170 CONTINUE(1),
171 // Provide information again
172 PROVIDE_INFORMATION_AGAIN(2),
173 // Cancel the install
174 CANCEL(3);
175
176 private int returnCode;
177 private ConfirmCode(int returnCode)
178 {
179 this.returnCode = returnCode;
180 }
181
182 /**
183 * Get the corresponding return code value.
184 *
185 * @return The corresponding return code value.
186 */
187 public int getReturnCode()
188 {
189 return returnCode;
190 }
191 }
192
193 private static final int LIMIT_KEYSTORE_PASSWORD_PROMPT = 7;
194
195 // Different variables we use when the user decides to provide data again.
196 private NewSuffixOptions.Type lastResetPopulateOption = null;
197
198 private String lastResetImportFile = null;
199
200 private String lastResetRejectedFile = null;
201
202 private String lastResetSkippedFile = null;
203
204 private Integer lastResetNumEntries = null;
205
206 private Boolean lastResetEnableSSL = null;
207
208 private Boolean lastResetEnableStartTLS = null;
209
210 private SecurityOptions.CertificateType lastResetCertType = null;
211
212 private String lastResetKeyStorePath = null;
213
214 private Boolean lastResetEnableWindowsService = null;
215
216 private Boolean lastResetStartServer = null;
217
218 /**
219 * The Logger.
220 */
221 static private final Logger LOG = Logger.getLogger(InstallDS.class.getName());
222
223 // The argument parser
224 private InstallDSArgumentParser argParser;
225
226 /**
227 * Constructor for the InstallDS object.
228 *
229 * @param out the print stream to use for standard output.
230 * @param err the print stream to use for standard error.
231 * @param in the input stream to use for standard input.
232 */
233 public InstallDS(PrintStream out, PrintStream err, InputStream in)
234 {
235 super(in, out, err);
236 }
237
238 /**
239 * The main method for the InstallDS CLI tool.
240 *
241 * @param args the command-line arguments provided to this program.
242 */
243
244 public static void main(String[] args)
245 {
246 int retCode = mainCLI(args, true, System.out, System.err, System.in);
247
248 System.exit(retCode);
249 }
250
251 /**
252 * Parses the provided command-line arguments and uses that information to
253 * run the setup tool.
254 *
255 * @param args the command-line arguments provided to this program.
256 *
257 * @return The error code.
258 */
259
260 public static int mainCLI(String[] args)
261 {
262 return mainCLI(args, true, System.out, System.err, System.in);
263 }
264
265 /**
266 * Parses the provided command-line arguments and uses that information to
267 * run the setup tool.
268 *
269 * @param args The command-line arguments provided to this
270 * program.
271 * @param initializeServer Indicates whether to initialize the server.
272 * @param outStream The output stream to use for standard output, or
273 * <CODE>null</CODE> if standard output is not
274 * needed.
275 * @param errStream The output stream to use for standard error, or
276 * <CODE>null</CODE> if standard error is not
277 * needed.
278 * @param inStream The input stream to use for standard input.
279 * @return The error code.
280 */
281
282 public static int mainCLI(String[] args, boolean initializeServer,
283 OutputStream outStream, OutputStream errStream, InputStream inStream)
284 {
285 PrintStream out;
286 if (outStream == null)
287 {
288 out = NullOutputStream.printStream();
289 }
290 else
291 {
292 out = new PrintStream(outStream);
293 }
294
295 System.setProperty(Constants.CLI_JAVA_PROPERTY, "true");
296
297 PrintStream err;
298 if (errStream == null)
299 {
300 err = NullOutputStream.printStream();
301 }
302 else
303 {
304 err = new PrintStream(errStream);
305 }
306
307 try {
308 QuickSetupLog.initLogFileHandler(
309 QuickSetupLog.isInitialized() ? null :
310 File.createTempFile(LOG_FILE_PREFIX, LOG_FILE_SUFFIX),
311 "org.opends.server.tools");
312 QuickSetupLog.disableConsoleLogging();
313 } catch (Throwable t) {
314 System.err.println("Unable to initialize log");
315 t.printStackTrace();
316 }
317
318 InstallDS install = new InstallDS(out, err, inStream);
319
320 return install.execute(args, initializeServer);
321 }
322
323 /**
324 * Parses the provided command-line arguments and uses that information to
325 * run the setup CLI.
326 *
327 * @param args the command-line arguments provided to this program.
328 * @param initializeServer Indicates whether to initialize the server.
329 *
330 * @return the return code (SUCCESSFUL, USER_DATA_ERROR or BUG).
331 */
332 public int execute(String[] args, boolean initializeServer)
333 {
334 argParser = new InstallDSArgumentParser(InstallDS.class.getName());
335 try
336 {
337 argParser.initializeArguments();
338 }
339 catch (ArgumentException ae)
340 {
341 Message message =
342 ToolMessages.ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
343 println(message);
344 return ErrorReturnCode.ERROR_UNEXPECTED.getReturnCode();
345 }
346
347 // Validate user provided data
348 try
349 {
350 argParser.parseArguments(args);
351 }
352 catch (ArgumentException ae)
353 {
354 Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
355 println(message);
356 println();
357 println(Message.raw(argParser.getUsage()));
358
359 return ErrorReturnCode.ERROR_USER_DATA.getReturnCode();
360 }
361
362 // Delete the log file that does not contain any information. The test only
363 // mode is called several times by the setup script and if we do not remove
364 // it we have a lot of empty log files.
365 if (argParser.testOnlyArg.isPresent())
366 {
367 try
368 {
369 QuickSetupLog.getLogFile().deleteOnExit();
370 }
371 catch (Throwable t)
372 {
373 LOG.log(Level.WARNING, "Error while trying to update the contents of "+
374 "the set-java-home file in test only mode: "+t, t);
375 }
376 // Test that we are running a compatible java 1.5 version.
377 try
378 {
379 Utils.checkJavaVersion();
380 }
381 catch (IncompatibleVersionException ive)
382 {
383 println(ive.getMessageObject());
384 return ReturnCode.JAVA_VERSION_INCOMPATIBLE.getReturnCode();
385 }
386 }
387
388 // If either the showUsage or testOnly or version arguments were provided,
389 // then we're done.
390 if (argParser.usageOrVersionDisplayed() ||
391 argParser.testOnlyArg.isPresent())
392 {
393 return ErrorReturnCode.SUCCESSFUL_NOP.getReturnCode();
394 }
395
396 try
397 {
398 checkInstallStatus();
399 }
400 catch (InitializationException ie)
401 {
402 println(ie.getMessageObject());
403 return ErrorReturnCode.ERROR_SERVER_ALREADY_INSTALLED.getReturnCode();
404 }
405
406 if (initializeServer)
407 {
408 try
409 {
410 initializeDirectoryServer(argParser.configFileArg.getValue(),
411 argParser.configClassArg.getValue());
412 }
413 catch (InitializationException ie)
414 {
415 println(ie.getMessageObject());
416 return ErrorReturnCode.ERROR_INITIALIZING_SERVER.getReturnCode();
417 }
418 }
419
420 boolean userApproved = false;
421
422 UserData uData = new UserData();
423 while (!userApproved)
424 {
425 try
426 {
427 if (isInteractive())
428 {
429 promptIfRequired(uData);
430 }
431 else
432 {
433 initializeUserDataWithParser(uData);
434 }
435 }
436 catch (UserDataException ude)
437 {
438 println(ude.getMessageObject());
439 if (isPasswordTriesError(ude.getMessageObject()))
440 {
441 return ErrorReturnCode.ERROR_PASSWORD_LIMIT.getReturnCode();
442 }
443 else
444 {
445 return ErrorReturnCode.ERROR_USER_DATA.getReturnCode();
446 }
447 }
448 if (isInteractive())
449 {
450 ConfirmCode confirm = askForConfirmation(uData);
451 switch (confirm)
452 {
453 case CONTINUE:
454 userApproved = true;
455 break;
456 case CANCEL:
457 LOG.log(Level.INFO, "User cancelled setup.");
458 return ErrorReturnCode.ERROR_USER_CANCELLED.getReturnCode();
459 default:
460 // Reset the arguments
461 try
462 {
463 resetArguments(uData);
464 }
465 catch (Throwable t)
466 {
467 LOG.log(Level.WARNING, "Error resetting arg parser: "+t, t);
468 }
469 userApproved = false;
470 }
471 }
472 else
473 {
474 userApproved = true;
475 }
476 }
477 System.setProperty(Constants.CLI_JAVA_PROPERTY, "true");
478 OfflineInstaller installer = new OfflineInstaller();
479 installer.setUserData(uData);
480 installer.setProgressMessageFormatter(formatter);
481 installer.addProgressUpdateListener(
482 new ProgressUpdateListener() {
483 public void progressUpdate(ProgressUpdateEvent ev) {
484 if (ev.getNewLogs() != null)
485 {
486 printProgress(ev.getNewLogs());
487 }
488 }
489 });
490 printlnProgress();
491
492 installer.run();
493
494 ApplicationException ue = installer.getRunError();
495
496 String cmd;
497 // Use this instead a call to Installation to avoid to launch a new JVM
498 // just to retrieve a path.
499 String root = Utils.getInstallPathFromClasspath();
500 if (SetupUtils.isWindows())
501 {
502 String binDir = Utils.getPath(root,
503 Installation.WINDOWS_BINARIES_PATH_RELATIVE);
504 cmd = Utils.getPath(binDir, Installation.WINDOWS_STATUSCLI_FILE_NAME);
505 }
506 else
507 {
508 String binDir = Utils.getPath(root,
509 Installation.UNIX_BINARIES_PATH_RELATIVE);
510 cmd = Utils.getPath(binDir, Installation.UNIX_STATUSCLI_FILE_NAME);
511 }
512 printlnProgress();
513 printProgress(INFO_INSTALLDS_STATUS_COMMAND_LINE.get(cmd));
514 printlnProgress();
515
516 if (ue != null)
517 {
518 return ue.getType().getReturnCode();
519 }
520 else
521 {
522 return ErrorReturnCode.SUCCESSFUL.getReturnCode();
523 }
524 }
525
526 /**
527 * Checks if the server is installed or not.
528 * @throws InitializationException if the server is already installed and
529 * configured or if the user did not accept to overwrite the existing
530 * databases.
531 */
532 private void checkInstallStatus() throws InitializationException
533 {
534 CurrentInstallStatus installStatus = new CurrentInstallStatus();
535 if (installStatus.canOverwriteCurrentInstall())
536 {
537 if (isInteractive())
538 {
539 println(installStatus.getInstallationMsg());
540 try
541 {
542 if (!confirmAction(INFO_CLI_DO_YOU_WANT_TO_CONTINUE.get(), true))
543 {
544 throw new InitializationException(Message.EMPTY, null);
545 }
546 }
547 catch (CLIException ce)
548 {
549 LOG.log(Level.SEVERE, "Unexpected error: "+ce, ce);
550 throw new InitializationException(Message.EMPTY, null);
551 }
552 }
553 else
554 {
555 println(installStatus.getInstallationMsg());
556 }
557 }
558 else if (installStatus.isInstalled())
559 {
560 throw new InitializationException(installStatus.getInstallationMsg(),
561 null);
562 }
563 }
564
565 /**
566 * Initialize the directory server to be able to perform the operations
567 * required during the installation.
568 * @param configFile the configuration file to be used to initialize the
569 * server.
570 * @param configClass the configuration class to be used to initialize the
571 * server.
572 * @throws InitializationException if there was an error during
573 * initialization.
574 */
575 private void initializeDirectoryServer(String configFile, String configClass)
576 throws InitializationException
577 {
578 printlnProgress();
579 printProgress(Message.raw(DirectoryServer.getVersionString()));
580 printlnProgress();
581 printProgress(INFO_INSTALLDS_INITIALIZING.get());
582 printlnProgress();
583
584 // Perform a base-level initialization that will be required to get
585 // minimal functionality like DN parsing to work.
586 DirectoryServer directoryServer = DirectoryServer.getInstance();
587 DirectoryServer.bootstrapClient();
588
589 try
590 {
591 DirectoryServer.initializeJMX();
592 }
593 catch (Throwable t)
594 {
595 Message message = ERR_INSTALLDS_CANNOT_INITIALIZE_JMX.get(
596 String.valueOf(configFile), t.getMessage());
597 throw new InitializationException(message, t);
598 }
599
600 try
601 {
602 directoryServer.initializeConfiguration(configClass, configFile);
603 }
604 catch (Throwable t)
605 {
606 Message message = ERR_INSTALLDS_CANNOT_INITIALIZE_CONFIG.get(
607 configFile, t.getMessage());
608 throw new InitializationException(message, t);
609 }
610
611 try
612 {
613 directoryServer.initializeSchema();
614 }
615 catch (Throwable t)
616 {
617 Message message = ERR_INSTALLDS_CANNOT_INITIALIZE_SCHEMA.get(
618 configFile, t.getMessage());
619 throw new InitializationException(message, t);
620 }
621 }
622
623 /**
624 * {@inheritDoc}
625 */
626 public boolean isQuiet()
627 {
628 return argParser.quietArg.isPresent();
629 }
630
631 /**
632 * {@inheritDoc}
633 */
634 public boolean isInteractive()
635 {
636 return !argParser.noPromptArg.isPresent();
637 }
638
639 /**
640 * {@inheritDoc}
641 */
642 @Override
643 public boolean isMenuDrivenMode() {
644 return true;
645 }
646
647 /**
648 * {@inheritDoc}
649 */
650 public boolean isScriptFriendly() {
651 return false;
652 }
653
654 /**
655 * {@inheritDoc}
656 */
657 public boolean isAdvancedMode() {
658 return false;
659 }
660
661
662 /**
663 * {@inheritDoc}
664 */
665 public boolean isVerbose() {
666 return argParser.verboseArg.isPresent();
667 }
668
669 /**
670 * This method updates the contents of a UserData object with what the user
671 * specified in the command-line. It assumes that it is being called in no
672 * prompt mode.
673 * @param uData the UserData object.
674 * @throws UserDataException if something went wrong checking the data.
675 */
676 private void initializeUserDataWithParser(UserData uData)
677 throws UserDataException
678 {
679 LinkedList<Message> errorMessages = new LinkedList<Message>();
680 uData.setConfigurationClassName(argParser.configClassArg.getValue());
681 uData.setConfigurationFile(argParser.configFileArg.getValue());
682 uData.setQuiet(isQuiet());
683 uData.setVerbose(isVerbose());
684 // Check the validity of the directory manager DNs
685 String dmDN = argParser.directoryManagerDNArg.getValue();
686
687 try
688 {
689 DN.decode(dmDN);
690 if (dmDN.trim().length() == 0)
691 {
692 errorMessages.add(ERR_INSTALLDS_EMPTY_DN_RESPONSE.get());
693 }
694 }
695 catch (Exception e)
696 {
697 Message message =
698 ERR_INSTALLDS_CANNOT_PARSE_DN.get(dmDN, e.getMessage());
699 errorMessages.add(message);
700 }
701 uData.setDirectoryManagerDn(dmDN);
702
703 uData.setDirectoryManagerPwd(argParser.getDirectoryManagerPassword());
704
705 // Check the validity of the base DNs
706 LinkedList<String> baseDNs = argParser.baseDNArg.getValues();
707 if (baseDNs.isEmpty())
708 {
709 baseDNs.add(argParser.baseDNArg.getDefaultValue());
710 }
711 for (String baseDN : baseDNs)
712 {
713 try
714 {
715 DN.decode(baseDN);
716 }
717 catch (Exception e)
718 {
719 Message message =
720 ERR_INSTALLDS_CANNOT_PARSE_DN.get(baseDN, e.getMessage());
721 errorMessages.add(message);
722 }
723 }
724
725 try
726 {
727 int ldapPort = argParser.ldapPortArg.getIntValue();
728 uData.setServerPort(ldapPort);
729 if (!argParser.skipPortCheckArg.isPresent())
730 {
731 // Check if the port can be used.
732 if (!SetupUtils.canUseAsPort(ldapPort))
733 {
734 Message message;
735 if (SetupUtils.isPriviledgedPort(ldapPort))
736 {
737 message = ERR_INSTALLDS_CANNOT_BIND_TO_PRIVILEGED_PORT.get(
738 ldapPort);
739 }
740 else
741 {
742 message = ERR_INSTALLDS_CANNOT_BIND_TO_PORT.get(ldapPort);
743 }
744 errorMessages.add(message);
745 }
746 }
747 if (argParser.jmxPortArg.isPresent())
748 {
749 int jmxPort = argParser.jmxPortArg.getIntValue();
750 uData.setServerJMXPort(jmxPort);
751 // Check if the port can be used.
752 if (!argParser.skipPortCheckArg.isPresent())
753 {
754 if (!SetupUtils.canUseAsPort(jmxPort))
755 {
756 Message message;
757 if (SetupUtils.isPriviledgedPort(jmxPort))
758 {
759 message = ERR_INSTALLDS_CANNOT_BIND_TO_PRIVILEGED_PORT.get(
760 jmxPort);
761 }
762 else
763 {
764 message = ERR_INSTALLDS_CANNOT_BIND_TO_PORT.get(jmxPort);
765 }
766 errorMessages.add(message);
767 }
768 }
769 }
770 }
771 catch (ArgumentException ae)
772 {
773 errorMessages.add(ae.getMessageObject());
774 }
775
776
777
778 NewSuffixOptions dataOptions;
779 if (argParser.importLDIFArg.isPresent())
780 {
781 // Check that the files exist
782 LinkedList<String> nonExistingFiles = new LinkedList<String>();
783 for (String file : argParser.importLDIFArg.getValues())
784 {
785 if (!Utils.fileExists(file))
786 {
787 nonExistingFiles.add(file);
788 }
789 }
790 if (nonExistingFiles.size() > 0)
791 {
792 errorMessages.add(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(
793 Utils.getStringFromCollection(nonExistingFiles, ", ")));
794 }
795 String rejectedFile = argParser.rejectedImportFileArg.getValue();
796 if (rejectedFile != null)
797 {
798 if (!Utils.canWrite(rejectedFile))
799 {
800 errorMessages.add(
801 ERR_INSTALLDS_CANNOT_WRITE_REJECTED.get(rejectedFile));
802 }
803 }
804 String skippedFile = argParser.skippedImportFileArg.getValue();
805 if (skippedFile != null)
806 {
807 if (!Utils.canWrite(skippedFile))
808 {
809 errorMessages.add(ERR_INSTALLDS_CANNOT_WRITE_SKIPPED.get(
810 skippedFile));
811 }
812 }
813 dataOptions = NewSuffixOptions.createImportFromLDIF(baseDNs,
814 argParser.importLDIFArg.getValues(),
815 rejectedFile, skippedFile);
816 }
817 else if (argParser.addBaseEntryArg.isPresent())
818 {
819 dataOptions = NewSuffixOptions.createBaseEntry(baseDNs);
820 }
821 else if (argParser.sampleDataArg.isPresent())
822 {
823 dataOptions = NewSuffixOptions.createAutomaticallyGenerated(baseDNs,
824 new Integer(argParser.sampleDataArg.getValue()));
825 }
826 else
827 {
828 dataOptions = NewSuffixOptions.createEmpty(baseDNs);
829 }
830 uData.setNewSuffixOptions(dataOptions);
831
832 // Check that the security data provided is valid.
833 String certNickname = argParser.certNicknameArg.getValue();
834 String pwd = argParser.getKeyStorePassword();
835 boolean enableSSL = argParser.ldapsPortArg.isPresent();
836 boolean enableStartTLS = argParser.enableStartTLSArg.isPresent();
837 int ldapsPort = -1;
838
839 try
840 {
841 ldapsPort = enableSSL ? argParser.ldapsPortArg.getIntValue() : -1;
842 }
843 catch (ArgumentException ae)
844 {
845 errorMessages.add(ae.getMessageObject());
846 }
847 if (enableSSL)
848 {
849 if (!argParser.skipPortCheckArg.isPresent())
850 {
851 if (!SetupUtils.canUseAsPort(ldapsPort))
852 {
853 if (SetupUtils.isPriviledgedPort(ldapsPort))
854 {
855 errorMessages.add(
856 ERR_INSTALLDS_CANNOT_BIND_TO_PRIVILEGED_PORT.get(ldapsPort));
857 }
858 else
859 {
860 errorMessages.add(ERR_INSTALLDS_CANNOT_BIND_TO_PORT.get(ldapsPort));
861 }
862 }
863 }
864 }
865 SecurityOptions securityOptions;
866 LinkedList<String> keystoreAliases = new LinkedList<String>();
867 if (argParser.generateSelfSignedCertificateArg.isPresent())
868 {
869 securityOptions = SecurityOptions.createSelfSignedCertificateOptions(
870 enableSSL, enableStartTLS, ldapsPort);
871 }
872 else if (argParser.useJavaKeyStoreArg.isPresent())
873 {
874 String path = argParser.useJavaKeyStoreArg.getValue();
875 checkCertificateInKeystore(SecurityOptions.CertificateType.JKS, path, pwd,
876 certNickname, errorMessages, keystoreAliases);
877 securityOptions = SecurityOptions.createJKSCertificateOptions(
878 path, pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
879 }
880 else if (argParser.usePkcs12Arg.isPresent())
881 {
882 String path = argParser.usePkcs12Arg.getValue();
883 checkCertificateInKeystore(SecurityOptions.CertificateType.PKCS12, path,
884 pwd, certNickname, errorMessages, keystoreAliases);
885 securityOptions = SecurityOptions.createPKCS12CertificateOptions(
886 path, pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
887 }
888 else if (argParser.usePkcs11Arg.isPresent())
889 {
890 checkCertificateInKeystore(SecurityOptions.CertificateType.PKCS11, null,
891 pwd, certNickname, errorMessages, keystoreAliases);
892 securityOptions = SecurityOptions.createPKCS11CertificateOptions(
893 pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
894 }
895 else
896 {
897 securityOptions = SecurityOptions.createNoCertificateOptions();
898 }
899 uData.setSecurityOptions(securityOptions);
900
901 uData.setEnableWindowsService(
902 argParser.enableWindowsServiceArg.isPresent());
903 uData.setStartServer(!argParser.doNotStartArg.isPresent());
904
905
906 if (errorMessages.size() > 0)
907 {
908 throw new UserDataException(null,
909 Utils.getMessageFromCollection(errorMessages,
910 formatter.getLineBreak().toString()));
911 }
912 }
913
914 /**
915 * This method updates the contents of a UserData object with what the user
916 * specified in the command-line. If the user did not provide explicitly some
917 * data or if the provided data is not valid, it prompts the user to provide
918 * it.
919 * @param uData the UserData object to be updated.
920 * @throws UserDataException if the user did not manage to provide the
921 * keystore password after a certain number of tries.
922 */
923 private void promptIfRequired(UserData uData) throws UserDataException
924 {
925 uData.setConfigurationClassName(argParser.configClassArg.getValue());
926 uData.setConfigurationFile(argParser.configFileArg.getValue());
927 uData.setQuiet(isQuiet());
928 uData.setVerbose(isVerbose());
929
930 promptIfRequiredForDirectoryManager(uData);
931 promptIfRequiredForPortData(uData);
932 promptIfRequiredForImportData(uData);
933 promptIfRequiredForSecurityData(uData);
934 promptIfRequiredForWindowsService(uData);
935 promptIfRequiredForStartServer(uData);
936 }
937
938 /**
939 * This method updates the contents of a UserData object with what the user
940 * specified in the command-line for the Directory Manager parameters.
941 * If the user did not provide explicitly some data or if the provided data is
942 * not valid, it prompts the user to provide it.
943 * @param uData the UserData object to be updated.
944 * @throws UserDataException if something went wrong checking the data.
945 */
946 private void promptIfRequiredForDirectoryManager(UserData uData)
947 throws UserDataException
948 {
949 LinkedList<String> dns = promptIfRequiredForDNs(
950 argParser.directoryManagerDNArg, INFO_INSTALLDS_PROMPT_ROOT_DN.get(),
951 true);
952 uData.setDirectoryManagerDn(dns.getFirst());
953
954 String pwd = argParser.getDirectoryManagerPassword();
955 int nTries = 0;
956 while (pwd == null)
957 {
958 if (nTries >= CONFIRMATION_MAX_TRIES)
959 {
960 throw new UserDataException(null,
961 ERR_TRIES_LIMIT_REACHED.get(CONFIRMATION_MAX_TRIES));
962 }
963 String pwd1 = null;
964 // Prompt for password and confirm.
965
966 while (pwd1 == null)
967 {
968 pwd1 = readPassword(INFO_INSTALLDS_PROMPT_ROOT_PASSWORD.get(), LOG);
969 if ((pwd1 == null) || "".equals(pwd1))
970 {
971 pwd1 = null;
972 println();
973 println(INFO_EMPTY_PWD.get());
974 println();
975 }
976 }
977 String pwd2 =
978 readPassword(INFO_INSTALLDS_PROMPT_CONFIRM_ROOT_PASSWORD.get(), LOG);
979
980 if (pwd1.equals(pwd2))
981 {
982 pwd = pwd1;
983 }
984 else
985 {
986 println();
987 println(ERR_INSTALLDS_PASSWORDS_DONT_MATCH.get());
988 }
989
990 nTries++;
991 }
992 uData.setDirectoryManagerPwd(pwd);
993 }
994
995 /**
996 * This method returns a list of DNs. It checks that the provided list of
997 * DNs actually contain some values. If no valid values are found it prompts
998 * the user to provide a valid DN.
999 * @param arg the Argument that the user provided to specify the DNs.
1000 * @param promptMsg the prompt message to be displayed.
1001 * @param includeLineBreak whether to include a line break before the first
1002 * prompt or not.
1003 * @return a list of valid DNs.
1004 * @throws UserDataException if something went wrong checking the data.
1005 */
1006 private LinkedList<String> promptIfRequiredForDNs(StringArgument arg,
1007 Message promptMsg, boolean includeLineBreak) throws UserDataException
1008 {
1009 LinkedList<String> dns = new LinkedList<String>();
1010
1011 boolean usedProvided = false;
1012 boolean firstPrompt = true;
1013 int nTries = 0;
1014 while (dns.isEmpty())
1015 {
1016 if (nTries >= CONFIRMATION_MAX_TRIES)
1017 {
1018 throw new UserDataException(null,
1019 ERR_TRIES_LIMIT_REACHED.get(CONFIRMATION_MAX_TRIES));
1020 }
1021 boolean prompted = false;
1022 if (usedProvided || !arg.isPresent())
1023 {
1024 if (firstPrompt && includeLineBreak)
1025 {
1026 println();
1027 }
1028 try
1029 {
1030 String dn = readInput(promptMsg, arg.getDefaultValue());
1031 firstPrompt = false;
1032 dns.add(dn);
1033 prompted = true;
1034 }
1035 catch (CLIException ce)
1036 {
1037 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1038 }
1039 }
1040 else
1041 {
1042 dns.addAll(arg.getValues());
1043 usedProvided = true;
1044 }
1045 LinkedList<String> toRemove = new LinkedList<String>();
1046 for (String dn : dns)
1047 {
1048 try
1049 {
1050 DN.decode(dn);
1051 if (dn.trim().length() == 0)
1052 {
1053 toRemove.add(dn);
1054 println(ERR_INSTALLDS_EMPTY_DN_RESPONSE.get());
1055 }
1056 }
1057 catch (Exception e)
1058 {
1059 toRemove.add(dn);
1060 Message message = prompted ? ERR_INSTALLDS_INVALID_DN_RESPONSE.get() :
1061 ERR_INSTALLDS_CANNOT_PARSE_DN.get(dn, e.getMessage());
1062 println(message);
1063 }
1064 }
1065 if (toRemove.size() > 0)
1066 {
1067 println();
1068 }
1069 dns.removeAll(toRemove);
1070 nTries++;
1071 }
1072 return dns;
1073 }
1074
1075 /**
1076 * This method updates the contents of a UserData object with what the user
1077 * specified in the command-line for the LDAP and JMX port parameters.
1078 * If the user did not provide explicitly some data or if the provided data is
1079 * not valid, it prompts the user to provide it.
1080 * Note: this method does not update nor check the LDAPS port.
1081 * @param uData the UserData object to be updated.
1082 */
1083 private void promptIfRequiredForPortData(UserData uData)
1084 {
1085 LinkedList<Integer> usedPorts = new LinkedList<Integer>();
1086 // Determine the LDAP port number.
1087 int ldapPort = promptIfRequiredForPortData(argParser.ldapPortArg,
1088 INFO_INSTALLDS_PROMPT_LDAPPORT.get(), usedPorts, true);
1089 uData.setServerPort(ldapPort);
1090 usedPorts.add(ldapPort);
1091 if (argParser.jmxPortArg.isPresent())
1092 {
1093 int jmxPort = promptIfRequiredForPortData(argParser.jmxPortArg,
1094 INFO_INSTALLDS_PROMPT_JMXPORT.get(), usedPorts, true);
1095 uData.setServerJMXPort(jmxPort);
1096 }
1097 else
1098 {
1099 uData.setServerJMXPort(-1);
1100 }
1101 }
1102
1103 /**
1104 * This method returns a valid port value. It checks that the provided
1105 * argument contains a valid port. If a valid port is not found it prompts
1106 * the user to provide a valid port.
1107 * @param arg the Argument that the user provided to specify the port.
1108 * @param promptMsg the prompt message to be displayed.
1109 * @param usedPorts the list of ports the user provided before for other
1110 * connection handlers.
1111 * @param includeLineBreak whether to include a line break before the first
1112 * prompt or not.
1113 * @return a valid port number.
1114 */
1115 private int promptIfRequiredForPortData(IntegerArgument portArg,
1116 Message promptMsg, Collection<Integer> usedPorts,
1117 boolean includeLineBreak)
1118 {
1119 int portNumber = -1;
1120 boolean usedProvided = false;
1121 boolean firstPrompt = true;
1122 while (portNumber == -1)
1123 {
1124 try
1125 {
1126 boolean prompted = false;
1127 if (usedProvided || !portArg.isPresent())
1128 {
1129 int defaultValue = -1;
1130 if (portArg.getDefaultValue() != null)
1131 {
1132 defaultValue = Integer.parseInt(portArg.getDefaultValue());
1133 }
1134 if (firstPrompt && includeLineBreak)
1135 {
1136 println();
1137 }
1138 portNumber = -1;
1139 while (portNumber == -1)
1140 {
1141 try
1142 {
1143 portNumber = readPort(promptMsg, defaultValue);
1144 }
1145 catch (CLIException ce)
1146 {
1147 portNumber = -1;
1148 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1149 }
1150 }
1151 prompted = true;
1152 firstPrompt = false;
1153 }
1154 else
1155 {
1156 portNumber = portArg.getIntValue();
1157 usedProvided = true;
1158 }
1159
1160 if (!argParser.skipPortCheckArg.isPresent())
1161 {
1162 // Check if the port can be used.
1163 if (!SetupUtils.canUseAsPort(portNumber))
1164 {
1165 Message message;
1166 if (SetupUtils.isPriviledgedPort(portNumber))
1167 {
1168 if (prompted || includeLineBreak)
1169 {
1170 println();
1171 }
1172 message = ERR_INSTALLDS_CANNOT_BIND_TO_PRIVILEGED_PORT.get(
1173 portNumber);
1174 println(message);
1175 portNumber = -1;
1176 }
1177 else
1178 {
1179 if (prompted || includeLineBreak)
1180 {
1181 println();
1182 }
1183 message = ERR_INSTALLDS_CANNOT_BIND_TO_PORT.get(portNumber);
1184 println(message);
1185 println();
1186 portNumber = -1;
1187 }
1188 }
1189 }
1190 if (portNumber != -1)
1191 {
1192 if (usedPorts.contains(portNumber))
1193 {
1194 Message message = ERR_CONFIGDS_PORT_ALREADY_SPECIFIED.get(
1195 String.valueOf(portNumber));
1196 println(message);
1197 println();
1198 portNumber = -1;
1199 }
1200 }
1201 }
1202 catch (ArgumentException ae)
1203 {
1204 println(ae.getMessageObject());
1205 }
1206 }
1207 return portNumber;
1208 }
1209
1210 /**
1211 * This method updates the contents of a UserData object with what the user
1212 * specified in the command-line for the base DN and data import parameters.
1213 * If the user did not provide explicitly some data or if the provided data is
1214 * not valid, it prompts the user to provide it.
1215 * @param uData the UserData object to be updated.
1216 * @throws UserDataException if something went wrong checking the data.
1217 */
1218 private void promptIfRequiredForImportData(UserData uData)
1219 throws UserDataException
1220 {
1221 // Check the validity of the base DNs
1222 LinkedList<String> baseDNs = promptIfRequiredForDNs(
1223 argParser.baseDNArg, INFO_INSTALLDS_PROMPT_BASEDN.get(), true);
1224
1225 NewSuffixOptions dataOptions;
1226 if (argParser.importLDIFArg.isPresent())
1227 {
1228 // Check that the files exist
1229 LinkedList<String> nonExistingFiles = new LinkedList<String>();
1230 LinkedList<String> importLDIFFiles = new LinkedList<String>();
1231 for (String file : argParser.importLDIFArg.getValues())
1232 {
1233 if (!Utils.fileExists(file))
1234 {
1235 nonExistingFiles.add(file);
1236 }
1237 else
1238 {
1239 importLDIFFiles.add(file);
1240 }
1241 }
1242 if (nonExistingFiles.size() > 0)
1243 {
1244 println();
1245 println(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(
1246 Utils.getStringFromCollection(nonExistingFiles, ", ")));
1247 }
1248 while (importLDIFFiles.isEmpty())
1249 {
1250 println();
1251 try
1252 {
1253 String path = readInput(INFO_INSTALLDS_PROMPT_IMPORT_FILE.get(),
1254 lastResetImportFile);
1255 if (!Utils.fileExists(path))
1256 {
1257 println();
1258 println(ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(path));
1259 }
1260 else
1261 {
1262 importLDIFFiles.add(path);
1263 }
1264 }
1265 catch (CLIException ce)
1266 {
1267 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1268 }
1269 }
1270 String rejectedFile = argParser.rejectedImportFileArg.getValue();
1271 if (rejectedFile != null)
1272 {
1273 while (!Utils.canWrite(rejectedFile))
1274 {
1275 println();
1276 println(ERR_INSTALLDS_CANNOT_WRITE_REJECTED.get(rejectedFile));
1277 println();
1278 try
1279 {
1280 rejectedFile =
1281 readInput(INFO_INSTALLDS_PROMPT_REJECTED_FILE.get(),
1282 lastResetRejectedFile);
1283 }
1284 catch (CLIException ce)
1285 {
1286 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1287 }
1288 }
1289 }
1290 String skippedFile = argParser.skippedImportFileArg.getValue();
1291 if (skippedFile != null)
1292 {
1293 while (!Utils.canWrite(skippedFile))
1294 {
1295 println();
1296 println(ERR_INSTALLDS_CANNOT_WRITE_SKIPPED.get(skippedFile));
1297 println();
1298 try
1299 {
1300 skippedFile =
1301 readInput(INFO_INSTALLDS_PROMPT_SKIPPED_FILE.get(),
1302 lastResetSkippedFile);
1303 }
1304 catch (CLIException ce)
1305 {
1306 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1307 }
1308 }
1309 }
1310
1311 dataOptions = NewSuffixOptions.createImportFromLDIF(baseDNs,
1312 importLDIFFiles, rejectedFile, skippedFile);
1313 }
1314 else if (argParser.addBaseEntryArg.isPresent())
1315 {
1316 dataOptions = NewSuffixOptions.createBaseEntry(baseDNs);
1317 }
1318 else if (argParser.sampleDataArg.isPresent())
1319 {
1320 int numUsers;
1321 try
1322 {
1323 numUsers = argParser.sampleDataArg.getIntValue();
1324 }
1325 catch (ArgumentException ae)
1326 {
1327 println();
1328 println(ae.getMessageObject());
1329 Message message = INFO_INSTALLDS_PROMPT_NUM_ENTRIES.get();
1330 numUsers = promptForInteger(message, 2000, 0, Integer.MAX_VALUE);
1331 }
1332 dataOptions = NewSuffixOptions.createAutomaticallyGenerated(baseDNs,
1333 numUsers);
1334 }
1335 else
1336 {
1337 final int POPULATE_TYPE_BASE_ONLY = 1;
1338 final int POPULATE_TYPE_LEAVE_EMPTY = 2;
1339 final int POPULATE_TYPE_IMPORT_FROM_LDIF = 3;
1340 final int POPULATE_TYPE_GENERATE_SAMPLE_DATA = 4;
1341
1342 int[] indexes = {POPULATE_TYPE_BASE_ONLY, POPULATE_TYPE_LEAVE_EMPTY,
1343 POPULATE_TYPE_IMPORT_FROM_LDIF, POPULATE_TYPE_GENERATE_SAMPLE_DATA};
1344 Message[] msgs = new Message[] {
1345 INFO_INSTALLDS_POPULATE_OPTION_BASE_ONLY.get(),
1346 INFO_INSTALLDS_POPULATE_OPTION_LEAVE_EMPTY.get(),
1347 INFO_INSTALLDS_POPULATE_OPTION_IMPORT_LDIF.get(),
1348 INFO_INSTALLDS_POPULATE_OPTION_GENERATE_SAMPLE.get()
1349 };
1350
1351 MenuBuilder<Integer> builder = new MenuBuilder<Integer>(this);
1352 builder.setPrompt(INFO_INSTALLDS_HEADER_POPULATE_TYPE.get());
1353
1354 for (int i=0; i<indexes.length; i++)
1355 {
1356 builder.addNumberedOption(msgs[i], MenuResult.success(indexes[i]));
1357 }
1358
1359 if (lastResetPopulateOption == null)
1360 {
1361 builder.setDefault(Message.raw(
1362 String.valueOf(POPULATE_TYPE_BASE_ONLY)),
1363 MenuResult.success(POPULATE_TYPE_BASE_ONLY));
1364 }
1365 else
1366 {
1367 switch (lastResetPopulateOption)
1368 {
1369 case LEAVE_DATABASE_EMPTY:
1370 builder.setDefault(Message.raw(
1371 String.valueOf(POPULATE_TYPE_LEAVE_EMPTY)),
1372 MenuResult.success(POPULATE_TYPE_LEAVE_EMPTY));
1373 break;
1374 case IMPORT_FROM_LDIF_FILE:
1375 builder.setDefault(Message.raw(
1376 String.valueOf(POPULATE_TYPE_IMPORT_FROM_LDIF)),
1377 MenuResult.success(POPULATE_TYPE_IMPORT_FROM_LDIF));
1378 break;
1379 case IMPORT_AUTOMATICALLY_GENERATED_DATA:
1380 builder.setDefault(Message.raw(
1381 String.valueOf(POPULATE_TYPE_GENERATE_SAMPLE_DATA)),
1382 MenuResult.success(POPULATE_TYPE_GENERATE_SAMPLE_DATA));
1383 break;
1384 default:
1385 builder.setDefault(Message.raw(
1386 String.valueOf(POPULATE_TYPE_BASE_ONLY)),
1387 MenuResult.success(POPULATE_TYPE_BASE_ONLY));
1388 }
1389 }
1390
1391 Menu<Integer> menu = builder.toMenu();
1392 int populateType;
1393 try
1394 {
1395 MenuResult<Integer> m = menu.run();
1396 if (m.isSuccess())
1397 {
1398 populateType = m.getValue();
1399 }
1400 else
1401 {
1402 // Should never happen.
1403 throw new RuntimeException();
1404 }
1405 }
1406 catch (CLIException ce)
1407 {
1408 populateType = POPULATE_TYPE_BASE_ONLY;
1409 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1410 }
1411
1412 if (populateType == POPULATE_TYPE_IMPORT_FROM_LDIF)
1413 {
1414 LinkedList<String> importLDIFFiles = new LinkedList<String>();
1415 while (importLDIFFiles.isEmpty())
1416 {
1417 Message message = INFO_INSTALLDS_PROMPT_IMPORT_FILE.get();
1418 println();
1419 try
1420 {
1421 String path = readInput(message, null);
1422 if (Utils.fileExists(path))
1423 {
1424 importLDIFFiles.add(path);
1425 }
1426 else
1427 {
1428 message = ERR_INSTALLDS_NO_SUCH_LDIF_FILE.get(path);
1429 println();
1430 println(message);
1431 }
1432 }
1433 catch (CLIException ce)
1434 {
1435 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1436 }
1437 }
1438 String rejectedFile = argParser.rejectedImportFileArg.getValue();
1439 if (rejectedFile != null)
1440 {
1441 while (!Utils.canWrite(rejectedFile))
1442 {
1443 println();
1444 println(
1445 ERR_INSTALLDS_CANNOT_WRITE_REJECTED.get(rejectedFile));
1446 println();
1447 try
1448 {
1449 rejectedFile =
1450 readInput(INFO_INSTALLDS_PROMPT_REJECTED_FILE.get(), null);
1451 }
1452 catch (CLIException ce)
1453 {
1454 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1455 }
1456 }
1457 }
1458 String skippedFile = argParser.skippedImportFileArg.getValue();
1459 if (skippedFile != null)
1460 {
1461 while (!Utils.canWrite(skippedFile))
1462 {
1463 println();
1464 println(ERR_INSTALLDS_CANNOT_WRITE_SKIPPED.get(skippedFile));
1465 println();
1466 try
1467 {
1468 skippedFile =
1469 readInput(INFO_INSTALLDS_PROMPT_SKIPPED_FILE.get(), null);
1470 }
1471 catch (CLIException ce)
1472 {
1473 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1474 }
1475 }
1476 }
1477 dataOptions = NewSuffixOptions.createImportFromLDIF(baseDNs,
1478 importLDIFFiles, rejectedFile, skippedFile);
1479 }
1480 else if (populateType == POPULATE_TYPE_GENERATE_SAMPLE_DATA)
1481 {
1482 Message message = INFO_INSTALLDS_PROMPT_NUM_ENTRIES.get();
1483 int defaultValue;
1484 if (lastResetNumEntries == null)
1485 {
1486 defaultValue = 2000;
1487 }
1488 else
1489 {
1490 defaultValue = lastResetNumEntries;
1491 }
1492 int numUsers = promptForInteger(message, defaultValue, 0,
1493 Integer.MAX_VALUE);
1494
1495 dataOptions = NewSuffixOptions.createAutomaticallyGenerated(baseDNs,
1496 numUsers);
1497 }
1498 else if (populateType == POPULATE_TYPE_LEAVE_EMPTY)
1499 {
1500 dataOptions = NewSuffixOptions.createEmpty(baseDNs);
1501 }
1502 else if (populateType == POPULATE_TYPE_BASE_ONLY)
1503 {
1504 dataOptions = NewSuffixOptions.createBaseEntry(baseDNs);
1505 }
1506 else
1507 {
1508 throw new IllegalStateException("Unexpected populateType: "+
1509 populateType);
1510 }
1511 }
1512 uData.setNewSuffixOptions(dataOptions);
1513 }
1514
1515 /**
1516 * This method updates the contents of a UserData object with what the user
1517 * specified in the command-line for the security parameters.
1518 * If the user did not provide explicitly some data or if the provided data is
1519 * not valid, it prompts the user to provide it.
1520 * @param uData the UserData object to be updated.
1521 * @throws UserDataException if the user did not manage to provide the
1522 * keystore password after a certain number of tries.
1523 */
1524 private void promptIfRequiredForSecurityData(UserData uData)
1525 throws UserDataException
1526 {
1527 // Check that the security data provided is valid.
1528 boolean enableSSL = false;
1529 boolean enableStartTLS = false;
1530 int ldapsPort = -1;
1531
1532 LinkedList<Integer> usedPorts = new LinkedList<Integer>();
1533 usedPorts.add(uData.getServerPort());
1534 if (uData.getServerJMXPort() != -1)
1535 {
1536 usedPorts.add(uData.getServerJMXPort());
1537 }
1538
1539 // Ask to enable SSL
1540 ldapsPort = -1;
1541
1542 if (!argParser.ldapsPortArg.isPresent())
1543 {
1544 println();
1545 try
1546 {
1547 boolean defaultValue = lastResetEnableSSL != null ? lastResetEnableSSL :
1548 false;
1549 enableSSL = confirmAction(INFO_INSTALLDS_PROMPT_ENABLE_SSL.get(),
1550 defaultValue);
1551 if (enableSSL)
1552 {
1553 ldapsPort = promptIfRequiredForPortData(argParser.ldapsPortArg,
1554 INFO_INSTALLDS_PROMPT_LDAPSPORT.get(), usedPorts, false);
1555 }
1556 }
1557 catch (CLIException ce)
1558 {
1559 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1560 }
1561 }
1562 else
1563 {
1564 ldapsPort = promptIfRequiredForPortData(argParser.ldapsPortArg,
1565 INFO_INSTALLDS_PROMPT_LDAPSPORT.get(), usedPorts, true);
1566 enableSSL = true;
1567 }
1568
1569 // Ask to enable Start TLS
1570 if (!argParser.enableStartTLSArg.isPresent())
1571 {
1572 println();
1573 try
1574 {
1575 boolean defaultValue = lastResetEnableStartTLS != null ?
1576 lastResetEnableStartTLS : false;
1577 enableStartTLS = confirmAction(INFO_INSTALLDS_ENABLE_STARTTLS.get(),
1578 defaultValue);
1579 }
1580 catch (CLIException ce)
1581 {
1582 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1583 }
1584 }
1585 else
1586 {
1587 enableStartTLS = true;
1588 }
1589
1590 SecurityOptions securityOptions;
1591 if (argParser.generateSelfSignedCertificateArg.isPresent())
1592 {
1593 securityOptions = SecurityOptions.createSelfSignedCertificateOptions(
1594 enableSSL, enableStartTLS, ldapsPort);
1595 }
1596 else if (argParser.useJavaKeyStoreArg.isPresent())
1597 {
1598 securityOptions =
1599 createSecurityOptionsPrompting(SecurityOptions.CertificateType.JKS,
1600 enableSSL, enableStartTLS, ldapsPort);
1601 }
1602 else if (argParser.usePkcs12Arg.isPresent())
1603 {
1604 securityOptions =
1605 createSecurityOptionsPrompting(SecurityOptions.CertificateType.PKCS12,
1606 enableSSL, enableStartTLS, ldapsPort);
1607 }
1608 else if (argParser.usePkcs11Arg.isPresent())
1609 {
1610 securityOptions =
1611 createSecurityOptionsPrompting(SecurityOptions.CertificateType.PKCS11,
1612 enableSSL, enableStartTLS, ldapsPort);
1613 }
1614 else
1615 {
1616 if (!enableSSL && !enableStartTLS)
1617 {
1618 // If the user did not want to enable SSL or start TLS do not ask
1619 // to create a certificate.
1620 securityOptions = SecurityOptions.createNoCertificateOptions();
1621 }
1622 else
1623 {
1624 final int SELF_SIGNED = 1;
1625 final int JKS = 2;
1626 final int PKCS12 = 3;
1627 final int PKCS11 = 4;
1628 int[] indexes = {SELF_SIGNED, JKS, PKCS12, PKCS11};
1629 Message[] msgs = {
1630 INFO_INSTALLDS_CERT_OPTION_SELF_SIGNED.get(),
1631 INFO_INSTALLDS_CERT_OPTION_JKS.get(),
1632 INFO_INSTALLDS_CERT_OPTION_PKCS12.get(),
1633 INFO_INSTALLDS_CERT_OPTION_PKCS11.get()
1634 };
1635
1636
1637 MenuBuilder<Integer> builder = new MenuBuilder<Integer>(this);
1638 builder.setPrompt(INFO_INSTALLDS_HEADER_CERT_TYPE.get());
1639
1640 for (int i=0; i<indexes.length; i++)
1641 {
1642 builder.addNumberedOption(msgs[i], MenuResult.success(indexes[i]));
1643 }
1644
1645 if (lastResetCertType == null)
1646 {
1647 builder.setDefault(Message.raw(String.valueOf(SELF_SIGNED)),
1648 MenuResult.success(SELF_SIGNED));
1649 }
1650 else
1651 {
1652 switch (lastResetCertType)
1653 {
1654 case JKS:
1655 builder.setDefault(Message.raw(String.valueOf(JKS)),
1656 MenuResult.success(JKS));
1657 break;
1658 case PKCS11:
1659 builder.setDefault(Message.raw(String.valueOf(PKCS11)),
1660 MenuResult.success(PKCS11));
1661 break;
1662 case PKCS12:
1663 builder.setDefault(Message.raw(String.valueOf(PKCS12)),
1664 MenuResult.success(PKCS12));
1665 break;
1666 default:
1667 builder.setDefault(Message.raw(String.valueOf(SELF_SIGNED)),
1668 MenuResult.success(SELF_SIGNED));
1669 }
1670 }
1671
1672 Menu<Integer> menu = builder.toMenu();
1673 int certType;
1674 try
1675 {
1676 MenuResult<Integer> m = menu.run();
1677 if (m.isSuccess())
1678 {
1679 certType = m.getValue();
1680 }
1681 else
1682 {
1683 // Should never happen.
1684 throw new RuntimeException();
1685 }
1686 }
1687 catch (CLIException ce)
1688 {
1689 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1690 certType = SELF_SIGNED;
1691 }
1692 if (certType == SELF_SIGNED)
1693 {
1694 securityOptions = SecurityOptions.createSelfSignedCertificateOptions(
1695 enableSSL, enableStartTLS, ldapsPort);
1696 }
1697 else if (certType == JKS)
1698 {
1699 securityOptions =
1700 createSecurityOptionsPrompting(SecurityOptions.CertificateType.JKS,
1701 enableSSL, enableStartTLS, ldapsPort);
1702 }
1703 else if (certType == PKCS12)
1704 {
1705 securityOptions =
1706 createSecurityOptionsPrompting(
1707 SecurityOptions.CertificateType.PKCS12, enableSSL,
1708 enableStartTLS, ldapsPort);
1709 }
1710 else if (certType == PKCS11)
1711 {
1712 securityOptions =
1713 createSecurityOptionsPrompting(
1714 SecurityOptions.CertificateType.PKCS11, enableSSL,
1715 enableStartTLS, ldapsPort);
1716 }
1717 else
1718 {
1719 throw new IllegalStateException("Unexpected cert type: "+ certType);
1720 }
1721 }
1722 }
1723 uData.setSecurityOptions(securityOptions);
1724 }
1725
1726 /**
1727 * This method updates the contents of a UserData object with what the user
1728 * specified in the command-line for the Windows Service parameters.
1729 * If the user did not provide explicitly the data, it prompts the user to
1730 * provide it.
1731 * @param uData the UserData object to be updated.
1732 */
1733 private void promptIfRequiredForWindowsService(UserData uData)
1734 {
1735 boolean enableService = false;
1736 // If we are in Windows ask if the server must run as a windows service.
1737 if (SetupUtils.isWindows())
1738 {
1739 if (argParser.enableWindowsServiceArg.isPresent())
1740 {
1741 enableService = true;
1742 }
1743 else
1744 {
1745 println();
1746 Message message = INFO_INSTALLDS_PROMPT_ENABLE_SERVICE.get();
1747 try
1748 {
1749 boolean defaultValue = (lastResetEnableWindowsService == null) ?
1750 false : lastResetEnableWindowsService;
1751 enableService = confirmAction(message, defaultValue);
1752 }
1753 catch (CLIException ce)
1754 {
1755 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1756 }
1757 }
1758 }
1759 uData.setEnableWindowsService(enableService);
1760 }
1761
1762 /**
1763 * This method updates the contents of a UserData object with what the user
1764 * specified in the command-line for the Directory Manager parameters.
1765 * If the user did not provide explicitly the data, it prompts the user to
1766 * provide it.
1767 * @param uData the UserData object to be updated.
1768 */
1769 private void promptIfRequiredForStartServer(UserData uData)
1770 {
1771 boolean startServer = false;
1772 if (!argParser.doNotStartArg.isPresent())
1773 {
1774 println();
1775 Message message = INFO_INSTALLDS_PROMPT_START_SERVER.get();
1776 try
1777 {
1778 boolean defaultValue = (lastResetStartServer == null) ?
1779 true : lastResetStartServer;
1780 startServer = confirmAction(message, defaultValue);
1781 }
1782 catch (CLIException ce)
1783 {
1784 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
1785 startServer = true;
1786 }
1787 }
1788 uData.setStartServer(startServer);
1789 }
1790
1791 /**
1792 * Checks that the provided parameters are valid to access an existing
1793 * keystore. This method adds the encountered errors to the provided
1794 * list of Message. It also adds the alias (nicknames) found to the provided
1795 * list of String.
1796 * @param type the type of keystore.
1797 * @param path the path of the keystore.
1798 * @param pwd the password (PIN) to access the keystore.
1799 * @param certNickname the certificate nickname that we are looking for (or
1800 * null if we just one to get the one that is in the keystore).
1801 * @param errorMessages the list that will be updated with the errors
1802 * encountered.
1803 * @param nicknameList the list that will be updated with the nicknames found
1804 * in the keystore.
1805 */
1806 private void checkCertificateInKeystore(SecurityOptions.CertificateType type,
1807 String path, String pwd, String certNickname,
1808 LinkedList<Message> errorMessages, LinkedList<String> nicknameList)
1809 {
1810 boolean errorWithPath = false;
1811 if (type != SecurityOptions.CertificateType.PKCS11)
1812 {
1813 File f = new File(path);
1814 if (!f.exists())
1815 {
1816 errorMessages.add(INFO_KEYSTORE_PATH_DOES_NOT_EXIST.get());
1817 errorWithPath = true;
1818 }
1819 else if (!f.isFile())
1820 {
1821 errorMessages.add(INFO_KEYSTORE_PATH_NOT_A_FILE.get());
1822 errorWithPath = true;
1823 }
1824 }
1825 boolean pwdProvided = true;
1826 if (pwd == null)
1827 {
1828 pwdProvided = false;
1829 errorMessages.add(INFO_ERROR_NO_KEYSTORE_PASSWORD.get());
1830 }
1831 else if (pwd.length() == 0)
1832 {
1833 pwdProvided = false;
1834 errorMessages.add(INFO_ERROR_EMPTY_KEYSTORE_PASSWORD.get());
1835 }
1836 if (!errorWithPath && pwdProvided)
1837 {
1838 try
1839 {
1840 CertificateManager certManager;
1841 switch (type)
1842 {
1843 case JKS:
1844 certManager = new CertificateManager(
1845 path,
1846 CertificateManager.KEY_STORE_TYPE_JKS,
1847 pwd);
1848 break;
1849
1850 case PKCS12:
1851 certManager = new CertificateManager(
1852 path,
1853 CertificateManager.KEY_STORE_TYPE_PKCS12,
1854 pwd);
1855 break;
1856
1857 case PKCS11:
1858 certManager = new CertificateManager(
1859 CertificateManager.KEY_STORE_PATH_PKCS11,
1860 CertificateManager.KEY_STORE_TYPE_PKCS11,
1861 pwd);
1862 break;
1863
1864 default:
1865 throw new IllegalArgumentException("Invalid type: "+type);
1866 }
1867 String[] aliases = certManager.getCertificateAliases();
1868 if ((aliases == null) || (aliases.length == 0))
1869 {
1870 // Could not retrieve any certificate
1871 switch (type)
1872 {
1873 case JKS:
1874 errorMessages.add(INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST.get());
1875 break;
1876
1877 case PKCS12:
1878 errorMessages.add(INFO_JKS_KEYSTORE_DOES_NOT_EXIST.get());
1879 break;
1880 case PKCS11:
1881 errorMessages.add(INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST.get());
1882 break;
1883 default:
1884 throw new IllegalArgumentException("Invalid type: "+type);
1885 }
1886 }
1887 else
1888 {
1889 for (int i=0; i<aliases.length; i++)
1890 {
1891 nicknameList.add(aliases[i]);
1892 }
1893 String aliasString = Utils.getStringFromCollection(nicknameList,
1894 ", ");
1895 if (certNickname != null)
1896 {
1897 // Check if the cert alias is in the list.
1898 boolean found = false;
1899 for (int i=0; i<aliases.length && !found; i++)
1900 {
1901 found = aliases[i].equalsIgnoreCase(certNickname);
1902 }
1903 if (!found)
1904 {
1905 errorMessages.add(ERR_INSTALLDS_CERTNICKNAME_NOT_FOUND.get(
1906 aliasString));
1907 }
1908 }
1909 else if (aliases.length > 1)
1910 {
1911 errorMessages.add(ERR_INSTALLDS_MUST_PROVIDE_CERTNICKNAME.get(
1912 aliasString));
1913 }
1914 }
1915 }
1916 catch (KeyStoreException ke)
1917 {
1918 // Could not access to the keystore: because the password is no good,
1919 // because the provided file is not a valid keystore, etc.
1920 switch (type)
1921 {
1922 case JKS:
1923 errorMessages.add(INFO_ERROR_ACCESSING_JKS_KEYSTORE.get());
1924 break;
1925
1926 case PKCS12:
1927 errorMessages.add(INFO_ERROR_ACCESSING_PKCS12_KEYSTORE.get());
1928 break;
1929 case PKCS11:
1930 errorMessages.add(INFO_ERROR_ACCESSING_PKCS11_KEYSTORE.get());
1931 break;
1932 default:
1933 throw new IllegalArgumentException("Invalid type: "+type);
1934 }
1935 }
1936 }
1937 }
1938
1939 /**
1940 * Creates a SecurityOptions object that corresponds to the provided
1941 * parameters. If the parameters are not valid, it prompts the user to
1942 * provide them.
1943 * @param type the keystore type.
1944 * @param enableSSL whether to enable SSL or not.
1945 * @param enableStartTLS whether to enable StartTLS or not.
1946 * @param ldapsPort the LDAPS port to use.
1947 * @return a SecurityOptions object that corresponds to the provided
1948 * parameters (or to what the user provided after being prompted).
1949 * @throws UserDataException if the user did not manage to provide the
1950 * keystore password after a certain number of tries.
1951 */
1952 private SecurityOptions createSecurityOptionsPrompting(
1953 SecurityOptions.CertificateType type, boolean enableSSL,
1954 boolean enableStartTLS, int ldapsPort) throws UserDataException
1955 {
1956 SecurityOptions securityOptions;
1957 String path;
1958 String certNickname = argParser.certNicknameArg.getValue();
1959 String pwd = argParser.getKeyStorePassword();
1960 if (pwd != null)
1961 {
1962 if (pwd.length() == 0)
1963 {
1964 pwd = null;
1965 }
1966 }
1967 Message pathPrompt;
1968 String defaultPathValue;
1969
1970 switch (type)
1971 {
1972 case JKS:
1973 path = argParser.useJavaKeyStoreArg.getValue();
1974 pathPrompt = INFO_INSTALLDS_PROMPT_JKS_PATH.get();
1975 defaultPathValue = argParser.useJavaKeyStoreArg.getValue();
1976 if (defaultPathValue == null)
1977 {
1978 defaultPathValue = lastResetKeyStorePath;
1979 }
1980 break;
1981 case PKCS11:
1982 path = null;
1983 defaultPathValue = null;
1984 pathPrompt = null;
1985 break;
1986 case PKCS12:
1987 path = argParser.usePkcs12Arg.getValue();
1988 defaultPathValue = argParser.usePkcs12Arg.getValue();
1989 if (defaultPathValue == null)
1990 {
1991 defaultPathValue = lastResetKeyStorePath;
1992 }
1993 pathPrompt = INFO_INSTALLDS_PROMPT_PKCS12_PATH.get();
1994 break;
1995 default:
1996 throw new IllegalStateException(
1997 "Called promptIfRequiredCertificate with invalid type: "+type);
1998 }
1999 LinkedList<Message> errorMessages = new LinkedList<Message>();
2000 LinkedList<String> keystoreAliases = new LinkedList<String>();
2001 boolean firstTry = true;
2002 int nPasswordPrompts = 0;
2003
2004 while ((errorMessages.size() > 0) || firstTry)
2005 {
2006 boolean prompted = false;
2007 if (errorMessages.size() > 0)
2008 {
2009 println();
2010 println(Utils.getMessageFromCollection(errorMessages,
2011 formatter.getLineBreak().toString()));
2012 }
2013
2014 if (type != SecurityOptions.CertificateType.PKCS11)
2015 {
2016 if (containsKeyStorePathErrorMessage(errorMessages) || (path == null))
2017 {
2018 println();
2019 try
2020 {
2021 path = readInput(pathPrompt, defaultPathValue);
2022 }
2023 catch (CLIException ce)
2024 {
2025 path = "";
2026 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
2027 }
2028
2029 prompted = true;
2030 if (pwd != null)
2031 {
2032 errorMessages.clear();
2033 keystoreAliases.clear();
2034 checkCertificateInKeystore(type, path, pwd, certNickname,
2035 errorMessages, keystoreAliases);
2036 if (!errorMessages.isEmpty())
2037 {
2038 // Reset password: this might be a new keystore
2039 pwd = null;
2040 }
2041 }
2042 }
2043 }
2044 if (containsKeyStorePasswordErrorMessage(errorMessages) ||
2045 (pwd == null))
2046 {
2047 if (!prompted)
2048 {
2049 println();
2050 }
2051 pwd = null;
2052 while (pwd == null)
2053 {
2054 if (nPasswordPrompts > LIMIT_KEYSTORE_PASSWORD_PROMPT)
2055 {
2056 throw new UserDataException(null,
2057 ERR_INSTALLDS_TOO_MANY_KEYSTORE_PASSWORD_TRIES.get(
2058 String.valueOf(LIMIT_KEYSTORE_PASSWORD_PROMPT)));
2059 }
2060 pwd = readPassword(
2061 INFO_INSTALLDS_PROMPT_KEYSTORE_PASSWORD.get(), LOG);
2062 nPasswordPrompts ++;
2063 }
2064 }
2065 if (containsCertNicknameErrorMessage(errorMessages))
2066 {
2067 if (!prompted)
2068 {
2069 println();
2070 }
2071 certNickname = promptForCertificateNickname(keystoreAliases);
2072 }
2073 errorMessages.clear();
2074 keystoreAliases.clear();
2075 checkCertificateInKeystore(type, path, pwd, certNickname, errorMessages,
2076 keystoreAliases);
2077 firstTry = false;
2078 }
2079 if (certNickname == null)
2080 {
2081 certNickname = keystoreAliases.getFirst();
2082 }
2083 switch (type)
2084 {
2085 case JKS:
2086 securityOptions = SecurityOptions.createJKSCertificateOptions(
2087 path, pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
2088 break;
2089 case PKCS12:
2090 securityOptions = SecurityOptions.createPKCS12CertificateOptions(
2091 path, pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
2092 break;
2093 case PKCS11:
2094 securityOptions = SecurityOptions.createPKCS11CertificateOptions(
2095 pwd, enableSSL, enableStartTLS, ldapsPort, certNickname);
2096 break;
2097 default:
2098 throw new IllegalStateException(
2099 "Called createSecurityOptionsPrompting with invalid type: "+type);
2100 }
2101 return securityOptions;
2102 }
2103
2104 /**
2105 * Tells if any of the error messages provided corresponds to a problem
2106 * with the key store path.
2107 * @param msgs the messages to analyze.
2108 * @return <CODE>true</CODE> if any of the error messages provided corresponds
2109 * to a problem with the key store path and <CODE>false</CODE> otherwise.
2110 */
2111 private boolean containsKeyStorePathErrorMessage(Collection<Message> msgs)
2112 {
2113 boolean found = false;
2114 for (Message msg : msgs)
2115 {
2116 if (msg.getDescriptor().equals(INFO_KEYSTORE_PATH_DOES_NOT_EXIST) ||
2117 msg.getDescriptor().equals(INFO_KEYSTORE_PATH_NOT_A_FILE) ||
2118 msg.getDescriptor().equals(INFO_JKS_KEYSTORE_DOES_NOT_EXIST) ||
2119 msg.getDescriptor().equals(INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST) ||
2120 msg.getDescriptor().equals(INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST) ||
2121 msg.getDescriptor().equals(INFO_ERROR_ACCESSING_JKS_KEYSTORE) ||
2122 msg.getDescriptor().equals(INFO_ERROR_ACCESSING_PKCS12_KEYSTORE) ||
2123 msg.getDescriptor().equals(INFO_ERROR_ACCESSING_PKCS11_KEYSTORE))
2124 {
2125 found = true;
2126 break;
2127 }
2128 }
2129 return found;
2130 }
2131
2132 /**
2133 * Tells if any of the error messages provided corresponds to a problem
2134 * with the key store password.
2135 * @param msgs the messages to analyze.
2136 * @return <CODE>true</CODE> if any of the error messages provided corresponds
2137 * to a problem with the key store password and <CODE>false</CODE> otherwise.
2138 */
2139 private boolean containsKeyStorePasswordErrorMessage(Collection<Message> msgs)
2140 {
2141 boolean found = false;
2142 for (Message msg : msgs)
2143 {
2144 if (msg.getDescriptor().equals(INFO_JKS_KEYSTORE_DOES_NOT_EXIST) ||
2145 msg.getDescriptor().equals(INFO_PKCS12_KEYSTORE_DOES_NOT_EXIST) ||
2146 msg.getDescriptor().equals(INFO_PKCS11_KEYSTORE_DOES_NOT_EXIST) ||
2147 msg.getDescriptor().equals(INFO_ERROR_ACCESSING_JKS_KEYSTORE) ||
2148 msg.getDescriptor().equals(INFO_ERROR_ACCESSING_PKCS12_KEYSTORE) ||
2149 msg.getDescriptor().equals(INFO_ERROR_ACCESSING_PKCS11_KEYSTORE) ||
2150 msg.getDescriptor().equals(INFO_ERROR_NO_KEYSTORE_PASSWORD) ||
2151 msg.getDescriptor().equals(INFO_ERROR_EMPTY_KEYSTORE_PASSWORD))
2152 {
2153 found = true;
2154 break;
2155 }
2156 }
2157 return found;
2158 }
2159
2160 /**
2161 * Tells if any of the error messages provided corresponds to a problem
2162 * with the certificate nickname.
2163 * @param msgs the messages to analyze.
2164 * @return <CODE>true</CODE> if any of the error messages provided corresponds
2165 * to a problem with the certificate nickname and <CODE>false</CODE>
2166 * otherwise.
2167 */
2168 private boolean containsCertNicknameErrorMessage(Collection<Message> msgs)
2169 {
2170 boolean found = false;
2171 for (Message msg : msgs)
2172 {
2173 if (msg.getDescriptor().equals(ERR_INSTALLDS_CERTNICKNAME_NOT_FOUND) ||
2174 msg.getDescriptor().equals(ERR_INSTALLDS_MUST_PROVIDE_CERTNICKNAME))
2175 {
2176 found = true;
2177 break;
2178 }
2179 }
2180 return found;
2181 }
2182
2183 /**
2184 * Tells if the error messages provided corresponds to a problem with the
2185 * password tries.
2186 * @param msg the message to analyze.
2187 * @return <CODE>true</CODE> if the error message provided corresponds to a
2188 * problem with the password tries and <CODE>false</CODE> otherwise.
2189 */
2190 private boolean isPasswordTriesError(Message msg)
2191 {
2192 return msg.getDescriptor().equals(
2193 ERR_INSTALLDS_TOO_MANY_KEYSTORE_PASSWORD_TRIES);
2194 }
2195
2196 /**
2197 * Interactively prompts (on standard output) the user to provide an integer
2198 * value. The answer provided must be parseable as an integer, and may be
2199 * required to be within a given set of bounds. It will keep prompting until
2200 * an acceptable value is given.
2201 *
2202 * @param prompt The prompt to present to the user.
2203 * @param defaultValue The default value to assume if the user presses ENTER
2204 * without typing anything, or <CODE>null</CODE> if
2205 * there should not be a default and the user must
2206 * explicitly provide a value.
2207 * @param lowerBound The lower bound that should be enforced, or
2208 * <CODE>null</CODE> if there is none.
2209 * @param upperBound The upper bound that should be enforced, or
2210 * <CODE>null</CODE> if there is none.
2211 *
2212 * @return The <CODE>int</CODE> value read from the user input.
2213 */
2214 private int promptForInteger(Message prompt, Integer defaultValue,
2215 Integer lowerBound, Integer upperBound)
2216 {
2217 int returnValue = -1;
2218 while (returnValue == -1)
2219 {
2220 String s;
2221 try
2222 {
2223 s = readInput(prompt, String.valueOf(defaultValue));
2224 }
2225 catch (CLIException ce)
2226 {
2227 s = "";
2228 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
2229 }
2230 if (s.equals(""))
2231 {
2232 if (defaultValue == null)
2233 {
2234 Message message = ERR_INSTALLDS_INVALID_INTEGER_RESPONSE.get();
2235 println(message);
2236 println();
2237 }
2238 else
2239 {
2240 returnValue = defaultValue;
2241 }
2242 }
2243 else
2244 {
2245 try
2246 {
2247 int intValue = Integer.parseInt(s);
2248 if ((lowerBound != null) && (intValue < lowerBound))
2249 {
2250 Message message =
2251 ERR_INSTALLDS_INTEGER_BELOW_LOWER_BOUND.get(lowerBound);
2252 println(message);
2253 println();
2254 }
2255 else if ((upperBound != null) && (intValue > upperBound))
2256 {
2257 Message message =
2258 ERR_INSTALLDS_INTEGER_ABOVE_UPPER_BOUND.get(upperBound);
2259 println(message);
2260 println();
2261 }
2262 else
2263 {
2264 returnValue = intValue;
2265 }
2266 }
2267 catch (NumberFormatException nfe)
2268 {
2269 Message message = ERR_INSTALLDS_INVALID_INTEGER_RESPONSE.get();
2270 println(message);
2271 println();
2272 }
2273 }
2274 }
2275 return returnValue;
2276 }
2277
2278 /**
2279 * Prompts the user to accept on the certificates that appears on the list
2280 * and returns the chosen certificate nickname.
2281 * @param nicknames the list of certificates the user must choose from.
2282 * @return the chosen certificate nickname.
2283 */
2284 private String promptForCertificateNickname(LinkedList<String> nicknames)
2285 {
2286 String nickname = null;
2287 while (nickname == null)
2288 {
2289 for (String n : nicknames)
2290 {
2291 try
2292 {
2293 if (confirmAction(INFO_INSTALLDS_PROMPT_CERTNICKNAME.get(n), true))
2294 {
2295 nickname = n;
2296 break;
2297 }
2298 }
2299 catch (CLIException ce)
2300 {
2301 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
2302 }
2303 }
2304 }
2305 return nickname;
2306 }
2307
2308 /**
2309 * This method asks the user to confirm to continue the setup. It basically
2310 * displays the information provided by the user and at the end proposes a
2311 * menu with the different options to choose from.
2312 * @param uData the UserData that the user provided.
2313 * @return the answer provided by the user: cancel setup, continue setup or
2314 * provide information again.
2315 */
2316 private ConfirmCode askForConfirmation(UserData uData)
2317 {
2318 ConfirmCode returnValue;
2319
2320 println();
2321 println();
2322 println(INFO_INSTALLDS_SUMMARY.get());
2323 Message[] labels =
2324 {
2325 INFO_SERVER_PORT_LABEL.get(),
2326 INFO_INSTALLDS_SERVER_JMXPORT_LABEL.get(),
2327 INFO_SERVER_SECURITY_LABEL.get(),
2328 INFO_SERVER_DIRECTORY_MANAGER_DN_LABEL.get(),
2329 INFO_DIRECTORY_DATA_LABEL.get()
2330 };
2331
2332 int jmxPort = uData.getServerJMXPort();
2333
2334 Message[] values =
2335 {
2336 Message.raw(String.valueOf(uData.getServerPort())),
2337 Message.raw(jmxPort != -1 ? String.valueOf(jmxPort) : null),
2338 Message.raw(
2339 QuickSetupStepPanel.getSecurityOptionsString(
2340 uData.getSecurityOptions(), false)),
2341 Message.raw(uData.getDirectoryManagerDn()),
2342 Message.raw(InstallReviewPanel.getDataDisplayString(uData)),
2343 };
2344 int maxWidth = 0;
2345 StringBuilder sb = new StringBuilder();
2346 for (Message l : labels)
2347 {
2348 maxWidth = Math.max(maxWidth, l.length());
2349 }
2350
2351 for (int i=0; i<labels.length; i++)
2352 {
2353 if (values[i] != null)
2354 {
2355 Message l = labels[i];
2356 sb.append(l.toString());
2357
2358 sb.append(" ");
2359 String[] lines = values[i].toString().split(Constants.LINE_SEPARATOR);
2360 for (int j=0; j<lines.length; j++)
2361 {
2362 if (j != 0)
2363 {
2364 sb.append(Constants.LINE_SEPARATOR);
2365 for (int k=0; k <= maxWidth; k++)
2366 {
2367 sb.append(" ");
2368 }
2369 }
2370 else
2371 {
2372 for (int k=0; k<maxWidth - l.length(); k++)
2373 {
2374 sb.append(" ");
2375 }
2376 }
2377 sb.append(lines[j].toString());
2378 }
2379 sb.append(Constants.LINE_SEPARATOR);
2380 }
2381 }
2382
2383 println(Message.raw(sb));
2384 println();
2385 if (uData.getStartServer())
2386 {
2387 println(INFO_INSTALLDS_START_SERVER.get());
2388 }
2389 else
2390 {
2391 println(INFO_INSTALLDS_DO_NOT_START_SERVER.get());
2392 }
2393
2394 println();
2395 println();
2396
2397 Message[] msgs = new Message[] {
2398 INFO_INSTALLDS_CONFIRM_INSTALL.get(),
2399 INFO_INSTALLDS_PROVIDE_DATA_AGAIN.get(),
2400 INFO_INSTALLDS_CANCEL.get()
2401 };
2402
2403 MenuBuilder<ConfirmCode> builder = new MenuBuilder<ConfirmCode>(this);
2404 builder.setPrompt(INFO_INSTALLDS_CONFIRM_INSTALL_PROMPT.get());
2405
2406 int i=0;
2407 for (ConfirmCode code : ConfirmCode.values())
2408 {
2409 builder.addNumberedOption(msgs[i], MenuResult.success(code));
2410 i++;
2411 }
2412
2413 builder.setDefault(Message.raw(
2414 String.valueOf(ConfirmCode.CONTINUE.getReturnCode())),
2415 MenuResult.success(ConfirmCode.CONTINUE));
2416
2417 Menu<ConfirmCode> menu = builder.toMenu();
2418
2419 try
2420 {
2421 MenuResult<ConfirmCode> m = menu.run();
2422 if (m.isSuccess())
2423 {
2424 returnValue = m.getValue();
2425 }
2426 else
2427 {
2428 // Should never happen.
2429 throw new RuntimeException();
2430 }
2431 }
2432 catch (CLIException ce)
2433 {
2434 returnValue = ConfirmCode.CANCEL;
2435 LOG.log(Level.WARNING, "Error reading input: "+ce, ce);
2436 }
2437 return returnValue;
2438 }
2439
2440 private void resetArguments(UserData uData)
2441 {
2442 argParser = new InstallDSArgumentParser(InstallDS.class.getName());
2443 try
2444 {
2445 argParser.initializeArguments();
2446 argParser.directoryManagerDNArg.setDefaultValue(
2447 uData.getDirectoryManagerDn());
2448 argParser.ldapPortArg.setDefaultValue(
2449 String.valueOf(uData.getServerPort()));
2450 int jmxPort = uData.getServerJMXPort();
2451 if (jmxPort != -1)
2452 {
2453 argParser.jmxPortArg.setDefaultValue(String.valueOf(jmxPort));
2454 }
2455 LinkedList<String> baseDNs = uData.getNewSuffixOptions().getBaseDns();
2456 if (!baseDNs.isEmpty())
2457 {
2458 argParser.baseDNArg.setDefaultValue(baseDNs.getFirst());
2459 }
2460 NewSuffixOptions suffixOptions = uData.getNewSuffixOptions();
2461 lastResetPopulateOption = suffixOptions.getType();
2462 if (lastResetPopulateOption ==
2463 NewSuffixOptions.Type.IMPORT_AUTOMATICALLY_GENERATED_DATA)
2464 {
2465 lastResetNumEntries = suffixOptions.getNumberEntries();
2466 }
2467 else if (lastResetPopulateOption ==
2468 NewSuffixOptions.Type.IMPORT_FROM_LDIF_FILE)
2469 {
2470 lastResetImportFile = suffixOptions.getLDIFPaths().getFirst();
2471 lastResetRejectedFile = suffixOptions.getRejectedFile();
2472 lastResetSkippedFile = suffixOptions.getSkippedFile();
2473 }
2474 SecurityOptions sec = uData.getSecurityOptions();
2475 if (sec.getEnableSSL())
2476 {
2477 argParser.ldapsPortArg.setDefaultValue(
2478 String.valueOf(sec.getSslPort()));
2479 }
2480 lastResetEnableSSL = sec.getEnableSSL();
2481 lastResetEnableStartTLS = sec.getEnableStartTLS();
2482 lastResetCertType = sec.getCertificateType();
2483 if (lastResetCertType == SecurityOptions.CertificateType.JKS ||
2484 lastResetCertType == SecurityOptions.CertificateType.PKCS11)
2485 {
2486 lastResetKeyStorePath = sec.getKeystorePath();
2487 }
2488 else
2489 {
2490 lastResetKeyStorePath = null;
2491 }
2492
2493 lastResetEnableWindowsService = uData.getEnableWindowsService();
2494 lastResetStartServer = uData.getStartServer();
2495 }
2496 catch (Throwable t)
2497 {
2498 LOG.log(Level.WARNING, "Error resetting arguments: "+t, t);
2499 }
2500 }
2501 }