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