/*
 * Copyright (C) 2020 - 2022 ScalAgent Distributed Technologies
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA.
 *
 * Initial developer(s): ScalAgent Distributed Technologies
 * Contributor(s): 
 */
package org.ow2.joram.tools.jmscheck;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

import javax.naming.InitialContext;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

import fr.dyade.aaa.common.Debug;

/**
 * This client regularly tests the state of a JMS connector and publishes the results to JMX.
 * <br><br>
 * usage: java -DJNDI_FILE=./jndi.properties -DPERIOD=1 -DTIMEOUT=5000<br>
 *         <dd>-DCF=cf -DQUEUE=queue</dd>
 *         <dd>-jar jmscheck.jar [help]</dd>
 * <br>
 * Options, set by Java environment variable ("-Dproperty=value" in command line):<ul>
 * <li>JNDI_FILE: Path of JNDI properties file. If not defined, Joram's default are
 *     used. "fr.dyade.aaa.jndi2.client.NamingContextFactory" for JNDI Factory,
 *     "localhost", and 16400 for host and port.<br>
 *     These values can be overloaded by specific properties below.</li>
 * <li>JNDI_FACTORY: Classname of the JNDI factory (cf java.naming.factory.initial
 *     property).</li>
 * <li>JNDI_HOST: Hostname ou IP address of JNDI server.</li>
 * <li>JNDI_PORT: Listening port of JNDI server.</li>
 * 
 * <li>PERIOD: Period between 2 checks, by default 60s.</li>
 * <li>TIMEOUT: Maximum amount of time to wait connecting and receiving messages,
 *     by default 10s.</li>
 * <br>
 * For each JMS connector to monitor there is 4 properties to define:<ul>
 * <li>CF: JNDI name of the ConnectionFactory to use.</li>
 * <li>QUEUE: Internal name of JMS destination.</li>
 * <li>USER: User name for authentication, if no defined uses the ConnectionFactory
 *     default.</li>
 * <li>PASS: Password for authentication, if no defined uses the ConnectionFactory
 *     default.</li>
 * </ul>
 * If there are multiple connectors to monitor, suffix each property with 1, 2,
 *     3, etc.
 * <br>
 * All these properties can be defined in a file whose name is given by the
 * CONF_FILE property. In this case the other properties defined in the command
 * line are ignored.
 */
public final class JMSCheck {
  public static final Logger logger = Debug.getLogger(JMSCheck.class.getName());

  public final static String GLOBAL_PERIOD = "PERIOD";
  public final static String GLOBAL_TIMEOUT = "TIMEOUT";
  
  public static final String JNDI_FILE = "JNDI_FILE";
  
  public static final String JNDI_FACTORY = "JNDI_FACTORY";
  public static final String JNDI_HOST = "JNDI_HOST";
  public static final String JNDI_PORT = "JNDI_PORT";
  
  public static final String CF_NAME = "CF";
  
  public static final String USER_NAME = "USER";
  public static final String PASSWORD = "PASS";
  
  public static final String QUEUE_NAME = "QUEUE";
  
  public static final String CONF_FILE = "CONF_FILE";

  static int period, timeout;
  static JMSStatus jmsStatus;

  public static void main(String[] args) throws Exception {
    if (args.length != 0) {
      System.out.println("usage: java -DJNDI_FILE=./jndi.properties -DPERIOD=1 -DTIMEOUT=5000\n" +
          "\t-DCF=cf -DQUEUE=queue\n" +
          "\t-jar joram-tools-jmscheck.jar [help]\n");

      System.err.println("Options, set by Java environment variable (\"-Dproperty=value\" in command line)");
      System.err.println("  - JNDI_FILE: Path of JNDI properties file. If not defined, Joram's default are\n" + 
          "    used. \"fr.dyade.aaa.jndi2.client.NamingContextFactory\" for JNDI Factory,\n" +
          "    \"localhost\", and 16400 for host and port.\n" +
          "    These values can be overloaded by specific properties below.");
      System.err.println("  - JNDI_FACTORY: Classname of the JNDI factory (cf java.naming.factory.initial\n" +
          "    property).");
      System.err.println("  - JNDI_HOST: Hostname ou IP address of JNDI server.");
      System.err.println("  - JNDI_PORT: Listening port of JNDI server.\n");
      
      System.err.println("  - PERIOD: Period between 2 checks, by default 60s.");
      System.err.println("  - TIMEOUT: Maximum amount of time to wait connecting and receiving messages,\n" +
          "    by default 10s.\n");
      
      System.err.println("For each JMS connector to monitor there is 4 properties to define:");
      System.err.println("  - CF: JNDI name of the ConnectionFactory to use.");
      System.err.println("  - QUEUE: Internal name of JMS destination.");
      System.err.println("  - USER: User name for authentication, if no defined uses the ConnectionFactory\n" +
          "    default.");
      System.err.println("  - PASS: Password for authentication, if no defined uses the ConnectionFactory\n" +
          "    default.");
      System.err.println("If there are multiple connectors to monitor, suffix each property with 1, 2, \n" +
          "    3, etc.\n");
      
      System.err.println("All these properties can be defined in a file whose name is given by the \n" + 
          "CONF_FILE property. In this case the other properties defined in the command\n" + 
          "line are ignored.");
      
      System.exit(0);
    }

    String conf = System.getProperty(CONF_FILE);
    Properties props = null;
    if (conf != null) {
      props = new Properties();
      try (FileInputStream fis = new FileInputStream(conf)) {
        props.load(fis);
      } catch (IOException exc) {
        System.err.println("Cannot read properties from \"" + conf + "\": " + exc.getMessage());
        System.exit(-1);
      }
    } else {
      props = System.getProperties();
    }
    
    // Gets the global period.
    period = JMSStatus.DFLT_PERIOD;
    String value = props.getProperty(GLOBAL_PERIOD);
    if (value != null) {
      try {
        period = Integer.parseInt(value);
      } catch (NumberFormatException exc) {
        logger.log(BasicLevel.WARN,
            "MqttCheckActivator.start: bad value for property " + GLOBAL_PERIOD + ", set to default.");
        period = JMSStatus.DFLT_PERIOD;
      }
    }
    if (period <= 0) return;

    // Gets the global timeout.
    timeout = JMSStatus.DFLT_TIMEOUT;
    value = props.getProperty(GLOBAL_TIMEOUT);
    if (value != null) {
      try {
        timeout = Integer.parseInt(value);
      } catch (NumberFormatException exc) {
        logger.log(BasicLevel.WARN,
            "MqttCheckActivator.start: bad value for property " + GLOBAL_TIMEOUT + ", set to default.");
        timeout = JMSStatus.DFLT_TIMEOUT;
      }
    }
    
    jmsStatus = new JMSStatus("Joram", period, timeout, null, 0, 0);

    String jndiFile = props.getProperty(JNDI_FILE);
    String jndiFactory = props.getProperty(JNDI_FACTORY);
    String jndiHost = props.getProperty(JNDI_HOST);
    String jndiPort = props.getProperty(JNDI_PORT);
    
    InitialContext ictx = jmsStatus.getInitialContext(jndiFile, jndiFactory, jndiHost, jndiPort);
    
    createConnector(props, jmsStatus, "", ictx);
    for (int i=1;; i++) {
      if (!createConnector(props, jmsStatus, "." + i, ictx)) break;
    }
    
    jmsStatus.start();
  }

  /**
   * Adds a new check component using the related properties.
   * 
   * @param check   The root component.
   * @param suffix  The property suffix.
   * @param ictx    The InitialContext to use.
   * @return True if the component is successively created.
   */
  private static boolean createConnector(Properties props, JMSStatus jmsStatus, String suffix, InitialContext ictx) {
    String cfname = props.getProperty(CF_NAME + suffix);
    if (cfname != null) {
      String user = props.getProperty(USER_NAME + suffix);
      String pass = props.getProperty(PASSWORD + suffix);
      String qname = props.getProperty(QUEUE_NAME + suffix);

      // TODO (AF): Allow to override period and timeout.
      jmsStatus.addConnectorStatus(cfname, ictx, user, pass, qname, period, timeout);
      return true;
    }
    return false;
  }
}
