ClientDriver.java

/*
** Module   : ClientDriver.java
** Abstract : general purpose client command line driver
**
** Copyright (c) 2005-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ----------------------------------Description-----------------------------------
** 001 NVS 20050827   @22390 Created this production level client driver.
** 002 NVS 20060110   @23849 Now the omission of the application key is allowed and causes
**                           StandardClient to be used.
** 003 GES 20060815   @28609 Refactored to remove duplicate code and share a common base class.
** 004 NVS 20061017   @30459 Switched to using connect() method instead of a callback approach.
** 005 GES 20061215   @31705 Changes in bootstrap config interface.
** 006 GES 20070111   @31780 Hard coded CHUI client processing here and removed some unnecessary
**                           processing/dead code.
** 007 GES 20070112   @31811 Removed initial debug level (it wasn't being used downstream) and
**                           added application args.
** 008 GES 20070115   @31826 Added support for conversation mode.
** 009 EVL 20070609   @34005 Adding explicit import of the class com.goldencode.p2j.net.Queue
**                           to eliminate conflict with the same class from java.util package
**                           to be able to compile for Java 6.
** 010 ECF 20071101   @35890 Refactored to accommodate net package mods. Eliminated
**                           ClientBootstrap dependencies, integrated SessionManager.
** 011 GES 20080409   @37911 Initialize remote directory access after the session is started.
** 012 GES 20090720   @43239 Better error handling for throwables.
** 013 GES 20090723   @43356 More error handling improvements and docs.
** 014 GES 20091103   @44304 Added a logging facility for detailed debugging.
** 015 GES 20110928          Moved core logic into a common class so it can be easily shared.
** 016 MAG 20140131          For web clients redirect the stderr to a file.
** 017 MAG 20140224          For batch clients redirect the stderr to a file.
** 018 MAG 20140707          Implements remote launcher (broker) a special P2J client.
** 019 GES 20141228          Rework to eliminate application arguments and to extend the client
**                           parameters to more use cases.
** 020 EVL 20160225          Javadoc fixes to make compatible with Oracle Java 8 for Solaris 10.
** 021 GES 20210827          Added driver name initialization and diagnostics output. 
**     GES 20211004 Changed log file name timestamp replacement to be a sortable date instead of millis.
**     EVL 20211222          Added implementation to get client driver options from server via socket.
**     HC  20220307          Added net:http_client:disable_ssl_certificate_validation to allow disabling of SSL
**                           certificate validation on HTTPS client connections.
**     GBB 20221209          Path to the background process stderr log file to be read from configs.
** 022 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
** 023 GBB 20230825          Adding an uncaught exceptions handler that logs before delegating to the 
**                           default one.
** 024 HC  20240222          Enabled JMX on FWD Client.
** 025 GBB 20240404          Close native Win terminal on ClientCore throwing exception.
** 026 GBB 20240415          Catch IllegalStateException on console termination (in Windows).
** 027 GBB 20240709          Removing methods related to receiving client options via socket.
** 028 GBB 20240729          Initialize CentralLogger after SecurityManager is created.
** 029 GBB 20240826          Removing single client.
*/

/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
** 
** Additional terms under GNU Affero GPL version 3 section 7:
** 
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
**   terms apply to the works covered under the License.  These additional terms
**   are non-permissive additional terms allowed under Section 7 of the GNU
**   Affero GPL version 3 and may not be removed by you.
** 
**   0. Attribution Requirement.
** 
**     You must preserve all legal notices or author attributions in the covered
**     work or Appropriate Legal Notices displayed by works containing the covered
**     work.  You may not remove from the covered work any author or developer
**     credit already included within the covered work.
** 
**   1. No License To Use Trademarks.
** 
**     This license does not grant any license or rights to use the trademarks
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
**     of Golden Code Development Corporation. You are not authorized to use the
**     name Golden Code, FWD, or the names of any author or contributor, for
**     publicity purposes without written authorization.
** 
**   2. No Misrepresentation of Affiliation.
** 
**     You may not represent yourself as Golden Code Development Corporation or FWD.
** 
**     You may not represent yourself for publicity purposes as associated with
**     Golden Code Development Corporation, FWD, or any author or contributor to
**     the covered work, without written authorization.
** 
**   3. No Misrepresentation of Source or Origin.
** 
**     You may not represent the covered work as solely your work.  All modified
**     versions of the covered work must be marked in a reasonable way to make it
**     clear that the modified work is not originating from Golden Code Development
**     Corporation or FWD.  All modified versions must contain the notices of
**     attribution required in this license.
*/

package com.goldencode.p2j.main;

import com.goldencode.p2j.jmx.*;
import com.goldencode.p2j.ui.chui.*;
import com.goldencode.p2j.ui.client.chui.driver.console.*;
import com.goldencode.p2j.ui.client.driver.*;
import com.goldencode.p2j.util.*;
import java.io.*;
import java.net.Socket;
import java.util.*;

import com.goldencode.p2j.cfg.*;
import com.goldencode.p2j.util.logging.*;
import com.goldencode.util.*;

import javax.management.*;

/**
 * General purpose command line driver to start a client process based on a bootstrap
 * configuration file and/or command line parameters. 
 * <p>
 * This driver performs a single logon to the server, invokes the application class and exits,
 * when the latter is done.
 * <p>
 * Configuration file can be plain (unencrypted) XML file or encrypted XML. If no password is 
 * given with the command line, the file is assumed to be plain XML.
 * <p>
 * Zero, one or two configuration files can be given as parameters along with their respective 
 * passwords. If not specified, it will default to "standard_client.xml" in the current
 * directory.
 * <p>
 * The two file approach is recommended as it allows for a natural way of separating common or
 * related configuration information (for all users or a group of users, normally supplied in the
 * first file), from the user-specific configuration information (usually supplied in the second
 * file). By merely specifying a different second file, another subject can use the application
 * while sharing the first configuration file. 
 * <p>
 * All entries in the bootstrap configuration can be overridden by a command line parameter using
 * the "category:group:key=value" syntax (see below).
 * <p>
 * The full syntax of the command line is:
 * <p>
 * <code>
 * java ClientDriver [-?]
 *                   [<i>config1</i>]
 *                   [-p <i>config1_file_password</i>]
 *                   [<i>config2</i>]
 *                   [-p <i>config2_file_password</i>]
 *                   [-d <i>initial_debuglevel</i>]
 *                   [[[category:]group:]key=value]...  
 * </code>
 * <p>
 * where arguments can be specified in any order.  The first password specified will be used
 * with the first config file specified and likewise with the second password and config file
 * respectively.
 * <p>
 * '-?' requests syntax help and exits.
 * <p>
 * Password can be given with the command line or entered interactively. One special value can 
 * be used: "?" (which causes an interactive password prompt to be displayed).
 * <p>
 * Arguments in the form "category:group:key=value" are bootstrap configuration overrides. They 
 * may be used to add or change values of the configuration keys in the specified category and
 * group. A short form of override (one that omits the category and group) is allowed after a
 * long form override and refers to the most recently specified category and group.
 */
public class ClientDriver
extends CommonDriver
{
   /** Logger. Should be instantiated after setting the logger to client mode. */
   private static CentralLogger LOG;
   
   {
      // initialize our protected members
      
      driverName = "ClientDriver";
      defCfg = "standard_client.xml";
      maxCfg = 2;
      
      helptxt = new String[]
      {
         "-----------------------P2J Client Driver-----------------------",
         "",
         "Syntax:",
         "",
         "java ClientDriver -?",
         "                  [config1]",
         "                  [-p config1_file_password]",
         "                  [config2]",
         "                  [-p config2_file_password]",
         "                  [-d initial_debug_level]",
         "                  [parameter_overrides]",
         "",
         "Where:",
         "",
         "-?                    = displays this help screen",
         "config                = a relative or absolute filename of a valid",
         "                        bootstrap configuration file for this",
         "                        client",
         "config_file_password  = the password to use to decrypt the config", 
         "                        file or '?' to force an interactive",
         "                        password prompt",
         "initial_debug_level   = an integer value that specifies the",
         "                        logging that should occur before server's",
         "                        main configuration (in the directory) can",
         "                        be read",
         "parameter_overrides   = configuration values that add to or",
         "                        replace values that can be specified in",
         "                        the bootstrap configuration; the override",
         "                        must take one of three forms:",
         "                           category:group:key=value",
         "                           group:key=value",
         "                           key=value",
         "                        (the shorter forms will use the most",
         "                        recently specified category and/or group",
         "                        as needed)",
         "",
         "All arguments can be specified in any order except '-?' which must", 
         "always appear first. The first password specified will be used with ",
         "the first config file specified and likewise with the second password ",
         "and config file respectively."
      };
   }

   /**
    * Allows a subclass to check if the given configuration matches any
    * required values.  Edits may be made if needed.
    *
    * @param    bc
    *           The configuration to be checked.
    */
   protected void checkConfig(BootstrapConfig bc)
   {
      bc.setServer(false);
      
      String convo = null;
      
      try
      {
         convo = bc.getConfigItem("net", "queue", "conversation");
      }
      
      catch (ConfigurationException ce)
      {
         // ignore
      }
         
      // default to conversation mode if not specified
      if (convo == null)
      {
         bc.setConfigItem("net", "queue", "conversation", "true");
      }
      
      // either way, force start thread to false (this client is going to
      // use its main thread as the conversation thread so we don't need
      // to start one
      bc.setConfigItem("net", "queue", "start_thread", "false");
   }
   
   /**
    * Runs the primary application using the preprocessed configuration and
    * target object.
    * 
    * @param    config
    *           The configuration to use.
    */
   protected void start(BootstrapConfig config)
   throws Exception
   {
      if (config.getBoolean("net", "http_client", "disable_ssl_certificate_validation", false))
      {
         Utils.disableClientSSLCertificateValidation();
      }

      if (config.getBoolean("client", "mode", "broker", false))
      {
         BrokerCore.start(config);
      }
      else
      {
         try
         {
            ClientCore.start(config, this::printDriverDetails);
         }
         catch (Throwable t)
         {
            if (PlatformHelper.isUnderWindowsFamily() &&
               ThinClient.getInstance().getOutputManager().getInstanceDriver() instanceof ConsoleDriver)
            {
               try
               {
                  ConsoleHelper.terminate();
               }
               catch (Throwable nativeThrowable)
               {
                  LOG.info("ConsoleHelper.terminate() execution has thrown an exception:", nativeThrowable);
               }
            }
            throw  t;
         }
      }
      
      // ensure that remaining non-daemon threads don't stop us from closing
      System.exit(0);
   }
   
   /**
    * The command line entry point.
    * 
    * @param     args
    *            The array of command-line parameters.
    */
   public static void main(String[] args)
   {
      CentralLogger.setMode(CentralLogger.Mode.CLIENT_BASE);
      LOG = CentralLogger.get(ClientDriver.class);

      setDefaultExceptionHandler(LOG);

      FwdClientJMX.init();

      ClientDriver client = new ClientDriver();
      
      // after our parent class processes the command line arguments, if there are no errors,
      // then the start() method will be called
      client.process(args);
   }
}