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