/*
 * JORAM: Java(TM) Open Reliable Asynchronous Messaging
 * Copyright (C) 2023- 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.ow2.joram.jndi;

import java.util.Enumeration;

import javax.naming.Context;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.StringRefAddr;

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

import fr.dyade.aaa.common.Debug;

/**
 * This class allows to retrieve JMS objects from JNDI.
 * 
 * TODO (AF): /!\ We should test these methods with joram.client.jms and joram.jakarta.jms objects, but also with third party JMS objects.
 */
public final class Helper {
  private static Logger logger = Debug.getLogger(Helper.class.getName());
  private static final boolean DEBUG = logger.isLoggable(BasicLevel.DEBUG);
  
  /**
   * Retrieve Jakarta/JMS administered objects from JNDI.
   * Dynamically transforms javax.jms objects in jakarta.jms.
   * 
   * @param jndiCtx
   * @param jndiName
   * @return
   * @throws Exception
   */
  public static Object retrieveJakartaJMSObjectFromJNDI(Context jndiCtx, String jndiName) throws Exception {
    if (DEBUG)
      logger.log(BasicLevel.DEBUG, "retrieveJakartaJMSObjectFromJNDI(" + jndiName + ")");
    
    if (jndiCtx == null) {
      logger.log(BasicLevel.ERROR, "retrieveJakartaJMSObjectFromJNDI(" + jndiName + "), JNDI context is null.");
      return null;
    }
    
    try {
      Object obj = jndiCtx.lookup(jndiName);
      if (DEBUG)
        logger.log(BasicLevel.DEBUG, "retrieveJakartaJMSObjectFromJNDI(" + jndiName + ") gets " + obj);
     
      // TODO (AF): May be we should test interface: jakarta.jms.ConnectionFactory and jakarta.jms.Destination
      if ((obj instanceof org.ow2.joram.jakarta.jms.ConnectionFactory) ||
          (obj instanceof org.ow2.joram.jakarta.jms.Destination)) {
        return obj;
      }
      
      if (obj instanceof org.objectweb.joram.client.jms.ConnectionFactory) {
        org.objectweb.joram.client.jms.ConnectionFactory cf1 = (org.objectweb.joram.client.jms.ConnectionFactory) obj;
        javax.naming.Reference ref1 = cf1.getReference();
        
        javax.naming.Reference ref2 = new Reference(ref1.getClassName().replace("objectweb.joram.client", "ow2.joram.jakarta"),
                                                    org.ow2.joram.jakarta.jms.admin.ObjectFactory.class.getName(), null);

        Enumeration<RefAddr> refAddrs = ref1.getAll();
        while (refAddrs.hasMoreElements()) {
          RefAddr ra = refAddrs.nextElement();
          if ("cf.reliableClass".equals(ra.getType())) {
            if (ra.getContent() != null)
              ref2.add(new StringRefAddr(ra.getType(), ((String) ra.getContent()).replace("objectweb.joram.client", "ow2.joram.jakarta")));
            else
              ref2.add(new StringRefAddr(ra.getType(), null));
          } else {
            ref2.add(ra);
          }
        }
        
        String cn = ref1.getClassName().replace("objectweb.joram.client", "ow2.joram.jakarta");
        org.ow2.joram.jakarta.jms.ConnectionFactory cf2 = null;
        try {
          cf2 = (org.ow2.joram.jakarta.jms.ConnectionFactory) Class.forName(cn).newInstance();
        } catch (Exception exc) {
          logger.log(BasicLevel.WARN,
                     "retrieveJakartaJMSObjectFromJNDI(" + jndiName + ") Can't transform to Jakarta/JMS: " + obj.getClass().getName());
          return obj;
        }
        cf2.fromReference(ref2);
        
        if (DEBUG)
          logger.log(BasicLevel.DEBUG, "retrieveJakartaJMSObjectFromJNDI(" + jndiName + ") returns " + cf2);

        return cf2;
      } else if (obj instanceof org.objectweb.joram.client.jms.Destination) {
        org.objectweb.joram.client.jms.Destination dest1 = (org.objectweb.joram.client.jms.Destination) obj;
        javax.naming.Reference ref = dest1.getReference();
        String cn = ref.getClassName().replace("objectweb.joram.client", "ow2.joram.jakarta");
        org.ow2.joram.jakarta.jms.Destination dest2 = null;
        try {
          dest2 = (org.ow2.joram.jakarta.jms.Destination) Class.forName(cn).newInstance();
        } catch (Exception exc) {
          logger.log(BasicLevel.WARN,
                     "retrieveJakartaJMSObjectFromJNDI(" + jndiName + ") Can't transform to Jakarta/JMS: " + obj.getClass().getName());
          return obj;
        }
        dest2.fromReference(ref);
        
        if (DEBUG)
          logger.log(BasicLevel.DEBUG, "retrieveJakartaJMSObjectFromJNDI(" + jndiName + ") returns " + dest2);

        return dest2;
      } else if (obj instanceof Reference) {
        Reference ref1 = (Reference) obj;
        
        javax.naming.Reference ref2 = new Reference(ref1.getClassName().replace("objectweb.joram.client", "ow2.joram.jakarta"),
                                                    org.ow2.joram.jakarta.jms.admin.ObjectFactory.class.getName(), null);

        Enumeration<RefAddr> refAddrs = ref1.getAll();
        while (refAddrs.hasMoreElements()) {
          RefAddr ra = refAddrs.nextElement();
          if ("cf.reliableClass".equals(ra.getType())) {
            if (ra.getContent() != null)
              ref2.add(new StringRefAddr(ra.getType(), ((String) ra.getContent()).replace("objectweb.joram.client", "ow2.joram.jakarta")));
            else
              ref2.add(new StringRefAddr(ra.getType(), null));
          } else {
            ref2.add(ra);
          }
        }

        org.ow2.joram.jakarta.jms.admin.AdministeredObject ao = null;
        try {
          Class<?> clazz = Class.forName(ref2.getClassName());
          ao = (org.ow2.joram.jakarta.jms.admin.AdministeredObject) clazz.newInstance();
          ao.fromReference(ref2);
        } catch (Exception exc) {
          if (DEBUG)
            logger.log(BasicLevel.ERROR, "retrieveJakartaJMSObjectFromJNDI(" + jndiName + ")", exc);
          else
            logger.log(BasicLevel.ERROR, "retrieveJakartaJMSObjectFromJNDI(" + jndiName + "): " + exc.getMessage());
        }
        
        if (DEBUG)
          logger.log(BasicLevel.DEBUG, "retrieveJakartaJMSObjectFromJNDI(" + jndiName + ") returns " + ao);

        return ao;
      }
      // Cannot transform, returns JNDI result.
      return obj;
    } finally {
      if (DEBUG)
        logger.log(BasicLevel.DEBUG, "retrieveJakartaJMSObjectFromJNDI(" + jndiName + ") ends.");
    }
  }
  
  /**
   * Retrieve Javax/JMS administered objects from JNDI.
   * Dynamically transforms javax.jms objects in jakarta.jms.
   * 
   * @param jndiCtx
   * @param jndiName
   * @return
   * @throws Exception
   */
  public static Object retrieveJavaxJMSObjectFromJNDI(Context jndiCtx, String jndiName) throws Exception {
    if (DEBUG)
      logger.log(BasicLevel.DEBUG, "retrieveJavaxJMSObjectFromJNDI(" + jndiName + ")");

    if (jndiCtx == null) {
      logger.log(BasicLevel.ERROR, "retrieveJakartaJMSObjectFromJNDI(" + jndiName + "), JNDI context is null.");
      return null;
    }
    
    try {
      Object obj = jndiCtx.lookup(jndiName);
      if (DEBUG)
        logger.log(BasicLevel.DEBUG, "retrieveJavaxJMSObjectFromJNDI(" + jndiName + ") gets " + obj);
     
      // TODO (AF): May be we should test interface: javax.jms.ConnectionFactory and javax.jms.Destination
      if ((obj instanceof org.objectweb.joram.client.jms.ConnectionFactory) ||
          (obj instanceof org.objectweb.joram.client.jms.Destination)) {
        return obj;
      }
      
      if (obj instanceof org.ow2.joram.jakarta.jms.ConnectionFactory) {
        org.ow2.joram.jakarta.jms.ConnectionFactory cf1 = (org.ow2.joram.jakarta.jms.ConnectionFactory) obj;
        javax.naming.Reference ref1 = cf1.getReference();
        
        javax.naming.Reference ref2 = new Reference(ref1.getClassName().replace("ow2.joram.jakarta", "objectweb.joram.client"),
                                                    org.objectweb.joram.client.jms.admin.ObjectFactory.class.getName(), null);

        Enumeration<RefAddr> refAddrs = ref1.getAll();
        while (refAddrs.hasMoreElements()) {
          RefAddr ra = refAddrs.nextElement();
          if ("cf.reliableClass".equals(ra.getType())) {
            if (ra.getContent() != null)
              ref2.add(new StringRefAddr(ra.getType(), ((String) ra.getContent()).replace("ow2.joram.jakarta", "objectweb.joram.client")));
            else
              ref2.add(new StringRefAddr(ra.getType(), null));
          } else {
            ref2.add(ra);
          }
        }
        
        String cn = ref1.getClassName().replace("ow2.joram.jakarta", "objectweb.joram.client");
        org.objectweb.joram.client.jms.ConnectionFactory cf2 = null;
        try {
          cf2 = (org.objectweb.joram.client.jms.ConnectionFactory) Class.forName(cn).newInstance();
        } catch (Exception exc) {
          logger.log(BasicLevel.WARN,
                     "retrieveJavaxJMSObjectFromJNDI(" + jndiName + ") Can't transform to Jakarta/JMS: " + obj.getClass().getName());
          return obj;
        }
        cf2.fromReference(ref2);
        
        if (DEBUG)
          logger.log(BasicLevel.DEBUG, "retrieveJavaxJMSObjectFromJNDI(" + jndiName + ") returns " + cf2);

        return cf2;
      } else if (obj instanceof org.ow2.joram.jakarta.jms.Destination) {
        org.ow2.joram.jakarta.jms.Destination dest1 = (org.ow2.joram.jakarta.jms.Destination) obj;
        javax.naming.Reference ref = dest1.getReference();
        String cn = ref.getClassName().replace("ow2.joram.jakarta", "objectweb.joram.client");
        org.objectweb.joram.client.jms.Destination dest2 = null;
        try {
          dest2 = (org.objectweb.joram.client.jms.Destination) Class.forName(cn).newInstance();
        } catch (Exception exc) {
          logger.log(BasicLevel.WARN,
                     "retrieveJavaxJMSObjectFromJNDI(" + jndiName + ") Can't transform to Jakarta/JMS: " + obj.getClass().getName());
          return obj;
        }
        dest2.fromReference(ref);
        
        if (DEBUG)
          logger.log(BasicLevel.DEBUG, "retrieveJavaxJMSObjectFromJNDI(" + jndiName + ") returns " + dest2);

        return dest2;
      } else if (obj instanceof Reference) {
        Reference ref1 = (Reference) obj;
        
        javax.naming.Reference ref2 = new Reference(ref1.getClassName().replace("ow2.joram.jakarta", "objectweb.joram.client"),
                                                    org.objectweb.joram.client.jms.admin.ObjectFactory.class.getName(), null);

        Enumeration<RefAddr> refAddrs = ref1.getAll();
        while (refAddrs.hasMoreElements()) {
          RefAddr ra = refAddrs.nextElement();
          if ("cf.reliableClass".equals(ra.getType())) {
            if (ra.getContent() != null)
              ref2.add(new StringRefAddr(ra.getType(), ((String) ra.getContent()).replace("ow2.joram.jakarta", "objectweb.joram.client")));
            else
              ref2.add(new StringRefAddr(ra.getType(), null));
          } else {
            ref2.add(ra);
          }
        }

        org.objectweb.joram.client.jms.admin.AdministeredObject ao = null;
        try {
          Class<?> clazz = Class.forName(ref2.getClassName());
          ao = (org.objectweb.joram.client.jms.admin.AdministeredObject) clazz.newInstance();
          ao.fromReference(ref2);
        } catch (Exception exc) {
          if (DEBUG)
            logger.log(BasicLevel.ERROR, "retrieveJavaxJMSObjectFromJNDI(" + jndiName + ")", exc);
          else
            logger.log(BasicLevel.ERROR, "retrieveJavaxJMSObjectFromJNDI(" + jndiName + "): " + exc.getMessage());
        }
        
        if (DEBUG)
          logger.log(BasicLevel.DEBUG, "retrieveJavaxJMSObjectFromJNDI(" + jndiName + ") returns " + ao);

        return ao;
      }
      // Cannot transform, returns JNDI result.
      return obj;
    } finally {
      if (DEBUG)
        logger.log(BasicLevel.DEBUG, "retrieveJakartaJMSObjectFromJNDI(" + jndiName + ") ends.");
    }
  }
}
