001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027package org.opends.guitools.controlpanel;
028
029import static com.forgerock.opendj.cli.Utils.*;
030
031import static org.opends.messages.AdminToolMessages.*;
032import static org.opends.messages.ToolMessages.*;
033
034import java.io.File;
035import java.io.PrintStream;
036
037import javax.swing.SwingUtilities;
038
039import org.forgerock.i18n.LocalizableMessage;
040import org.forgerock.i18n.slf4j.LocalizedLogger;
041import org.opends.guitools.controlpanel.util.ControlPanelLog;
042import org.opends.messages.AdminToolMessages;
043import org.opends.quicksetup.ui.UIFactory;
044import org.opends.quicksetup.util.Utils;
045import org.opends.server.types.InitializationException;
046import org.opends.server.util.BuildVersion;
047import org.opends.server.util.DynamicConstants;
048
049import com.forgerock.opendj.cli.ArgumentException;
050
051/**
052 * The class that is invoked directly by the control-panel command-line.  This
053 * class basically displays a splash screen and then calls the methods in
054 * ControlPanel.  It also is in charge of detecting whether there are issues
055 * with the
056 *
057 */
058public class ControlPanelLauncher
059{
060  private static ControlPanelArgumentParser  argParser;
061
062  /** Prefix for log files. */
063  public static final String LOG_FILE_PREFIX = "opendj-control-panel-";
064
065  /** Suffix for log files. */
066  public static final String LOG_FILE_SUFFIX = ".log";
067
068  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
069
070  /**
071   * Main method invoked by the control-panel script.
072   * @param args the arguments.
073   */
074  public static void main(String[] args)
075  {
076    try {
077      ControlPanelLog.initLogFileHandler(
078          File.createTempFile(LOG_FILE_PREFIX, LOG_FILE_SUFFIX));
079    } catch (Throwable t) {
080      System.err.println("Unable to initialize log");
081      t.printStackTrace();
082    }
083
084    argParser = new ControlPanelArgumentParser(
085        ControlPanelLauncher.class.getName(),
086        INFO_CONTROL_PANEL_LAUNCHER_USAGE_DESCRIPTION.get());
087    //  Validate user provided data
088    try
089    {
090      argParser.initializeArguments();
091      argParser.parseArguments(args);
092    }
093    catch (ArgumentException ae)
094    {
095      argParser.displayMessageAndUsageReference(System.err, ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
096      System.exit(ErrorReturnCode.ERROR_PARSING_ARGS.getReturnCode());
097    }
098
099    // If we should just display usage or version information,
100    // then print it and exit.
101    if (argParser.usageOrVersionDisplayed())
102    {
103      System.exit(ErrorReturnCode.SUCCESSFUL_NOP.getReturnCode());
104    }
105
106    // Checks the version - if upgrade required, the tool is unusable
107    if (!argParser.isRemote())
108    {
109      try
110      {
111        BuildVersion.checkVersionMismatch();
112      }
113      catch (InitializationException e)
114      {
115        System.err.println(wrapText(e.getMessage(), MAX_LINE_WIDTH));
116        System.exit(ErrorReturnCode.ERROR_UNEXPECTED.getReturnCode());
117      }
118    }
119
120    if (!argParser.usageOrVersionDisplayed())
121    {
122      int exitCode = launchControlPanel(args);
123      if (exitCode != 0)
124      {
125        String logFileName = null;
126        if (ControlPanelLog.getLogFile() != null)
127        {
128          logFileName = ControlPanelLog.getLogFile().toString();
129        }
130        if (logFileName != null)
131        {
132          System.err.println(wrapText(
133              ERR_CONTROL_PANEL_LAUNCHER_GUI_LAUNCH_FAILED_DETAILS.get(
134                  logFileName),
135                  Utils.getCommandLineMaxLineWidth()));
136        }
137        else
138        {
139          System.err.println(wrapText(
140              ERR_CONTROL_PANEL_LAUNCHER_GUI_LAUNCH_FAILED.get(),
141              Utils.getCommandLineMaxLineWidth()));
142        }
143        System.exit(exitCode);
144      }
145    }
146  }
147
148  /**
149   * Launches the graphical status panel. It is launched in a
150   * different thread that the main thread because if we have a problem with the
151   * graphical system (for instance the DISPLAY environment variable is not
152   * correctly set) the native libraries will call exit. However if we launch
153   * this from another thread, the thread will just be killed.
154   *
155   * This code also assumes that if the call to ControlPanelSplashScreen.main
156   * worked (and the splash screen was displayed) we will never get out of it
157   * (we will call a System.exit() when we close the graphical status dialog).
158   *
159   * @param  args the arguments used to call the
160   *         ControlPanelSplashScreen main method.
161   * @return 0 if everything worked fine, or 1 if we could not display properly
162   *         the ControlPanelSplashScreen.
163   */
164  private static int launchControlPanel(final String[] args)
165  {
166    final int[] returnValue = { -1 };
167    Thread t = new Thread(new Runnable()
168    {
169      @Override
170      public void run()
171      {
172        try
173        {
174          try
175          {
176            initLookAndFeel();
177          }
178          catch (Throwable t)
179          {
180            logger.warn(LocalizableMessage.raw("Error setting look and feel: "+t, t));
181          }
182
183          ControlPanelSplashScreen.main(args);
184          returnValue[0] = 0;
185        }
186        catch (Throwable t)
187        {
188          if (ControlPanelLog.isInitialized())
189          {
190            logger.warn(LocalizableMessage.raw("Error launching GUI: "+t));
191            StringBuilder buf = new StringBuilder();
192            while (t != null)
193            {
194              for (StackTraceElement aStack : t.getStackTrace()) {
195                buf.append(aStack).append("\n");
196              }
197
198              t = t.getCause();
199              if (t != null)
200              {
201                buf.append("Root cause:\n");
202              }
203            }
204            logger.warn(LocalizableMessage.raw(buf));
205          }
206        }
207      }
208    });
209    /*
210     * This is done to avoid displaying the stack that might occur if there are
211     * problems with the display environment.
212     */
213    PrintStream printStream = System.err;
214    System.setErr(Utils.getEmptyPrintStream());
215    t.start();
216    try
217    {
218      t.join();
219    }
220    catch (InterruptedException ie)
221    {
222      /* An error occurred, so the return value will be -1.  We got nothing to
223      do with this exception. */
224    }
225    System.setErr(printStream);
226    return returnValue[0];
227  }
228
229  private static void initLookAndFeel() throws Throwable
230  {
231//  Setup MacOSX native menu bar before AWT is loaded.
232    LocalizableMessage title = Utils.getCustomizedObject("INFO_CONTROL_PANEL_TITLE",
233        AdminToolMessages.INFO_CONTROL_PANEL_TITLE.get(
234        DynamicConstants.PRODUCT_NAME), LocalizableMessage.class);
235    Utils.setMacOSXMenuBar(title);
236    UIFactory.initializeLookAndFeel();
237  }
238}
239
240
241/**
242 * The enumeration containing the different return codes that the command-line
243 * can have.
244 *
245 */
246enum ErrorReturnCode
247{
248  /**
249   * Successful display of the status.
250   */
251  SUCCESSFUL(0),
252  /**
253   * We did no have an error but the status was not displayed (displayed
254   * version or usage).
255   */
256  SUCCESSFUL_NOP(0),
257  /**
258   * Unexpected error (potential bug).
259   */
260  ERROR_UNEXPECTED(1),
261  /**
262   * Cannot parse arguments.
263   */
264  ERROR_PARSING_ARGS(2),
265  /**
266   * User cancelled (for instance not accepting the certificate proposed) or
267   * could not use the provided connection parameters in interactive mode.
268   */
269  USER_CANCELLED_OR_DATA_ERROR(3),
270  /**
271   * This occurs for instance when the authentication provided by the user is
272   * not valid.
273   */
274  ERROR_READING_CONFIGURATION_WITH_LDAP(4);
275
276  private int returnCode;
277  private ErrorReturnCode(int returnCode)
278  {
279    this.returnCode = returnCode;
280  }
281
282  /**
283   * Returns the corresponding return code value.
284   * @return the corresponding return code value.
285   */
286  public int getReturnCode()
287  {
288    return returnCode;
289  }
290}
291
292/**
293 * The splash screen for the control panel.
294 *
295 */
296class ControlPanelSplashScreen extends org.opends.quicksetup.SplashScreen
297{
298  private static final long serialVersionUID = 4472839063380302713L;
299
300  private static ControlPanel controlPanel;
301
302  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
303
304  /**
305   * The main method for this class.
306   * It can be called from the event thread and outside the event thread.
307   * @param args arguments to be passed to the method ControlPanel.initialize
308   */
309  public static void main(String[] args)
310  {
311    ControlPanelSplashScreen screen = new ControlPanelSplashScreen();
312    screen.display(args);
313  }
314
315
316  /**
317   * This methods constructs the ControlPanel object.
318   * This method assumes that is being called outside the event thread.
319   * @param args arguments to be passed to the method ControlPanel.initialize.
320   */
321  @Override
322  protected void constructApplication(String[] args)
323  {
324    try
325    {
326      controlPanel = new ControlPanel();
327      controlPanel.initialize(args);
328    } catch (Throwable t)
329    {
330      if (ControlPanelLog.isInitialized())
331      {
332        logger.error(LocalizableMessage.raw("Error launching GUI: "+t, t));
333      }
334      InternalError error =
335        new InternalError("Failed to invoke initialize method");
336      error.initCause(t);
337      throw error;
338    }
339  }
340
341  /**
342   * This method displays the StatusPanel dialog.
343   * @see org.opends.guitools.controlpanel.ControlPanel#createAndDisplayGUI()
344   * This method assumes that is being called outside the event thread.
345   */
346  @Override
347  protected void displayApplication()
348  {
349    Runnable runnable = new Runnable()
350    {
351      @Override
352      public void run()
353      {
354        try
355        {
356          logger.info(LocalizableMessage.raw("going to call createAndDisplayGUI."));
357          controlPanel.createAndDisplayGUI();
358          logger.info(LocalizableMessage.raw("called createAndDisplayGUI."));
359        } catch (Throwable t)
360        {
361          logger.error(LocalizableMessage.raw("Error displaying GUI: "+t, t));
362          InternalError error =
363            new InternalError("Failed to invoke display method");
364          error.initCause(t);
365          throw error;
366        }
367      }
368    };
369    if (SwingUtilities.isEventDispatchThread())
370    {
371      runnable.run();
372    }
373    else
374    {
375      try
376      {
377        SwingUtilities.invokeAndWait(runnable);
378      }
379      catch (Throwable t)
380      {
381        logger.error(LocalizableMessage.raw("Error calling SwingUtilities.invokeAndWait: "+t,
382            t));
383        InternalError error =
384          new InternalError(
385              "Failed to invoke SwingUtilities.invokeAndWait method");
386        error.initCause(t);
387        throw error;
388      }
389    }
390  }
391}
392
393