/*
 * JORAM: Java(TM) Open Reliable Asynchronous Messaging
 * Copyright (C) 2011 - 2025 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.objectweb.joram.mom.dest.jms;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

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

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

/**
 * The {@link JMSConnections} service handles the list of known AMQP
 * servers, in order to keep live connections with them.
 */
public final class JMSConnections implements JMSConnectionsMBean {
  private static final Logger logger = Debug.getLogger(JMSConnections.class.getName());

  static final String SAVE_FILE_NAME = "JmsCnx";

  public final static String TRANSIENT_CONFIGURATION_PROPERTY = "org.ow2.joram.jmsbridge.TRANSIENT_CONFIGURATION";
  public final static boolean TRANSIENT_CONFIGURATION = AgentServer.getBoolean(TRANSIENT_CONFIGURATION_PROPERTY);

  private Map<String, JMSModule> servers = new HashMap<String, JMSModule>();

  JMSConnections() {}

  /**
   * Reads the configuration for JMS bridge connections from persistence.
   * @return true if the configuration is read, false otherwise.
   */
  boolean readSavedConf() {
    try {
      servers = (Map<String, JMSModule>) AgentServer.getTransaction().load(SAVE_FILE_NAME);
    } catch (Exception exc) {
      if (logger.isLoggable(BasicLevel.DEBUG))
        logger.log(BasicLevel.ERROR, "Error while loading configuration for JMS bridge connections: ", exc);
      else
        logger.log(BasicLevel.ERROR, "Error while loading configuration for JMS bridge connections: " + exc.getMessage());
    }
    
    if (servers == null) {
      servers = new HashMap<String, JMSModule>();
      return false;
    }
    
    Iterator<JMSModule> iter = servers.values().iterator();
    while (iter.hasNext()) {
      iter.next().startLiveConnection();
    }
    return true;
  }
  
  /**
   * Saves the configuration for JMS bridge connections to persistence.
   */
  void saveConf() {
    if (!TRANSIENT_CONFIGURATION) {
      try {
        if (! AgentServer.isEngineThread())
          AgentServer.getTransaction().begin();

        AgentServer.getTransaction().save((HashMap) servers, SAVE_FILE_NAME);

        if (! AgentServer.isEngineThread())
          AgentServer.getTransaction().commit(true);
      } catch (IOException exc) {
        if (logger.isLoggable(BasicLevel.DEBUG))
          logger.log(BasicLevel.ERROR, "Error while persisting configuration for JMS bridge connections: ", exc);
        else
          logger.log(BasicLevel.ERROR, "Error while persisting configuration for JMS bridge connections: " + exc.getMessage());
      }
    }
  }

  /** {@inheritDoc} */
  public void addServer(String name, String cnxFactoryName) {
    addServer(name, cnxFactoryName, null, null);
  }

  /** {@inheritDoc} */
  public void addServer(String name, String cnxFactoryName, String jndiFactoryClass, String jndiUrl) {
    addServer(name, cnxFactoryName, jndiFactoryClass, jndiUrl, null, null);
  }

  /** {@inheritDoc} */
  public void addServer(String name, String cnxFactoryName, String jndiFactoryClass, String jndiUrl, String user,
      String password) {
    addServer(name, cnxFactoryName, jndiFactoryClass, jndiUrl, user, password, null);
  }
  
  /** {@inheritDoc} */
  public void addServer(String name,
                        String cnxFactoryName,
                        String jndiFactoryClass,
                        String jndiUrl,
                        String user,
                        String password,
                        String clientID) {
  	if (logger.isLoggable(BasicLevel.DEBUG)) {
			logger.log(BasicLevel.DEBUG,
			           "JMSConnection.addServer(" + name + ", " + cnxFactoryName + ", " + jndiFactoryClass + ", " + jndiUrl + ", " + user + ", ****, " + clientID + ')');
  	}
  	
    if (name == null) {
      int i = 0;
      do {
        name = "cnx" + i; i += 1;
      } while (servers.containsKey(name));
      logger.log(BasicLevel.WARN,
                 "Cannot add an unamed JMSConnection, set name to \"" + name  + "\".");
    }
			
    synchronized (servers) {
      if (!servers.containsKey(name)) {
        JMSModule cnx = new JMSModule(name, cnxFactoryName, jndiFactoryClass, jndiUrl, user, password, clientID);
        cnx.startLiveConnection();
        servers.put(name, cnx);
      } else {
        logger.log(BasicLevel.ERROR,
                   "Cannot add a JMSConnection with an already defined name: " + name  + ".");
      }
    }
    
    saveConf();
  }

  /** {@inheritDoc} */
  public void deleteServer(String name) {
  	if (logger.isLoggable(BasicLevel.DEBUG)) {
			logger.log(BasicLevel.DEBUG, "JMSConnection.deleteServer(" + name + ')');
  	}
    synchronized (servers) {
      JMSModule cnx = servers.remove(name);
      if (cnx != null) {
        cnx.stopLiveConnection();
        saveConf();
      }
    }
  }

  /** {@inheritDoc} */
  public String[] getServerNames() {
    return servers.keySet().toArray(new String[servers.size()]);
  }

  /**
   * Gets the list of currently opened connections.
   * 
   * @return the list of usable connections.
   */
  public List<JMSModule> getConnections() {
    List<JMSModule> connections = new ArrayList<JMSModule>();
    synchronized (servers) {
      for (JMSModule server : servers.values()) {
        if (server.isConnectionOpen()) {
          connections.add(server);
        }
      }
    }
    return connections;
  }

  public void stop() {
    synchronized (servers) {
      for (JMSModule server : servers.values()) {
        server.stopLiveConnection();
      }
    }
  }

}
