/*
 * 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 javax.naming.InitialContext;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

import fr.dyade.aaa.agent.AgentServer;
import fr.dyade.aaa.common.Debug;

/**
 * This component regularly tests the state of a JMS connector and publishes the results to JMX.
 * <br>
 * It is configured through a set of OSGi properties:<ul>
 * <li>org.ow2.joram.check.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>org.ow2.joram.check.jndi.factory: Classname of the JNDI factory (cf java.naming.factory.initial
 *     property).</li>
 * <li>org.ow2.joram.check.jndi.host: Hostname ou IP address of JNDI server.</li>
 * <li>org.ow2.joram.check.jndi.port: Listening port of JNDI server.</li>
 * <li>org.ow2.joram.check.period: Period between 2 checks, by default 60s.</li>
 * <li>org.ow2.joram.check.timeout: Maximum amount of time to wait connecting and receiving
 *     messages, by default 10s.</li>
 * </ul>
 * For each JMS connector to monitor there is 4 properties to define:<ul>
 * <li>org.ow2.joram.check.cf: JNDI name of the ConnectionFactory to use.</li>
 * <li>org.ow2.joram.check.queue: Internal name of JMS destination.</li>
 * <li>org.ow2.joram.check.user: User name for authentication, if no defined uses the
 *     ConnectionFactory default.</li>
 * <li>org.ow2.joram.check.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, etc.
 */

public final class Activator implements BundleActivator {
  public static final Logger logger = Debug.getLogger(Activator.class.getName());
  
  public final static String CHECK_PREFIX = "org.ow2.joram.check.";

  public final static String GLOBAL_PERIOD = CHECK_PREFIX + "period";
  public final static String GLOBAL_TIMEOUT = CHECK_PREFIX + "timeout";
  
  public final static String DUMP_FILE = CHECK_PREFIX + "dump.file";
  public final static String DUMP_THRESHOLD = CHECK_PREFIX + "dump.threshold";
  public final static String DUMP_DELAY = CHECK_PREFIX + "dump.delay";
  
  public final static String JNDI_FILE = CHECK_PREFIX + "jndi.file";
  public final static String JNDI_FACTORY = CHECK_PREFIX + "jndi.factory";
  public final static String JNDI_HOST = CHECK_PREFIX + "jndi.host";
  public final static String JNDI_PORT = CHECK_PREFIX + "jndi.port";
  
  public static final String CF_NAME = CHECK_PREFIX + "cf";
  
  public static final String USER_NAME = CHECK_PREFIX + "user";
  public static final String PASSWORD = CHECK_PREFIX + "password";
  
  public static final String QUEUE_NAME = CHECK_PREFIX + "queue";
  
  private static BundleContext context;
  
  public Activator() {
  }

  int period, timeout;
  JMSStatus jmsStatus;
  
  @Override
  public void start(BundleContext context) throws Exception {
    Activator.context = context;
    
    // Gets the global period.
    period = JMSStatus.DFLT_PERIOD;
    String value = context.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 = context.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;
      }
    }

    // Gets the pathname of the dump file.
    String dumpFilePath = context.getProperty(DUMP_FILE);
    if (dumpFilePath == null) {
      dumpFilePath = JMSStatus.DFLT_DUMP_FILE;
    }
    
    // Gets the threshold to generate a dump file.
    int threshold = JMSStatus.DFLT_THRESHOLD;
    value = context.getProperty(DUMP_THRESHOLD);
    if (value != null) {
      try {
        threshold = Integer.parseInt(value);
      } catch (NumberFormatException exc) {
        logger.log(BasicLevel.WARN,
            "MqttCheckActivator.start: bad value for property " + DUMP_THRESHOLD + ", set to default.");
        threshold = JMSStatus.DFLT_THRESHOLD;
      }
    }
    
    // Gets the minimal delay before to generate anew a dump file.
    int delay = JMSStatus.DFLT_DELAY;
    value = context.getProperty(DUMP_DELAY);
    if (value != null) {
      try {
        delay = Integer.parseInt(value);
      } catch (NumberFormatException exc) {
        logger.log(BasicLevel.WARN,
            "MqttCheckActivator.start: bad value for property " + DUMP_DELAY + ", set to default.");
        delay = JMSStatus.DFLT_DELAY;
      }
    }
    
    jmsStatus = new JMSStatus("Joram#" + AgentServer.getServerId(), period, timeout, dumpFilePath, threshold, delay);

    String jndiFile = context.getProperty(JNDI_FILE);
    String jndiFactory = context.getProperty(JNDI_FACTORY);
    String jndiHost = context.getProperty(JNDI_HOST);
    String jndiPort = context.getProperty(JNDI_PORT);
    
    InitialContext ictx = jmsStatus.getInitialContext(jndiFile, jndiFactory, jndiHost, jndiPort);
    
    createConnector(jmsStatus, "", ictx);
    for (int i=1;; i++) {
      if (!createConnector(jmsStatus, "." + i, ictx)) break;
    }
    
    jmsStatus.start();
  }
  
//  public synchronized Object lookup(String name) throws NamingException {
//    if (logger.isLoggable(BasicLevel.DEBUG))
//      logger.log(BasicLevel.DEBUG, "Helper.lookup " + name);
//
//    ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
//    try {
//      Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
//
//      if (ictx == null)
//        ictx = new InitialContext(jndiProps);
//      return ictx.lookup(name);
//    } finally {
//      Thread.currentThread().setContextClassLoader(originalContextClassLoader);
//    }
//  }

  /**
   * 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 boolean createConnector(JMSStatus check, String suffix, InitialContext ictx) {
    String cfname = context.getProperty(CF_NAME + suffix);
    if (cfname != null) {
      String user = context.getProperty(USER_NAME + suffix);
      String pass = context.getProperty(PASSWORD + suffix);
      String qname = context.getProperty(QUEUE_NAME + suffix);

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

  @Override
  public void stop(BundleContext context) throws Exception {
    Activator.context = null;
    jmsStatus.stop();
  }
}
