/**
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.deployment.spi;

import org.jboss.deployment.SerializableDeploymentInfo;
import org.jboss.logging.Logger;

import javax.enterprise.deploy.shared.ModuleType;
import javax.enterprise.deploy.spi.TargetModuleID;
import javax.enterprise.deploy.spi.exceptions.TargetException;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.net.InetAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;

/**
 * A Target that deploys using the JMX adaptor to communicate with the
 * MainDeployer using file URLs to the deployments.
 *
 * @author thomas.diesler@jboss.org
 * @author Scott.Stark@jboss.com
 * @version $Revision: 1.1.1.1.4.1 $
 */
public class JMXTarget implements JBossTarget
{
   private static final Logger log = Logger.getLogger(JMXTarget.class);
   /**
    * The default RMIAdaptor JNDI location
    */
   private static final String DEFAULT_ADAPTOR_PATH = "/jmx/invoker/RMIAdaptor";
   /**
    * The MainDeployer JMX object name string
    */
   private static final String MAIN_DEPLOYER = "jboss.system:service=MainDeployer";

   private URI deployURI;
   private Properties jndiEnv;

   public JMXTarget(URI deployURI)
   {
      log.debug("new JMXTarget: " + deployURI);
      try
      {
         String scheme = deployURI.getScheme();
         String host = deployURI.getHost();
         int port = deployURI.getPort();
         String path = deployURI.getPath();
         String query = deployURI.getQuery();

         StringBuffer uri = new StringBuffer(scheme + "://");
         uri.append(host != null ? host : InetAddress.getLocalHost().getHostName());
         uri.append(port > 0 ? ":" + port : "");
         uri.append(path != null && path.length() > 0 ? path : DEFAULT_ADAPTOR_PATH);
         uri.append(query != null ? "?" + query : "");

         deployURI = new URI(uri.toString());
         log.debug("URI changed to: " + deployURI);
      }
      catch (Exception e)
      {
         log.error(e);
      }

      this.deployURI = deployURI;
   }

   /**
    * Get the target's description
    * 
    * @return the description
    */
   public String getDescription()
   {
      return "JBoss JMX deployment target";
   }

   /**
    * Get the target's name
    * 
    * @return the name
    */
   public String getName()
   {
      return deployURI.toString();
   }

   /**
    * Get the target's host name
    */
   public String getHostName()
   {
      return deployURI.getHost();
   }

   /**
    * Deploy a given module
    */
   public void deploy(TargetModuleID targetModuleID)
           throws Exception
   {
      MBeanServerConnection server = getMBeanServerConnection();
      ObjectName mainDeployer = new ObjectName(MAIN_DEPLOYER);
      String url = targetModuleID.getModuleID();
      Object[] args = {url};
      String[] sig = {String.class.getName()};
      log.info("Starting deploy of: " + url);
      server.invoke(mainDeployer, "deploy", args, sig);
   }

   /**
    * Start a given module
    */
   public void start(TargetModuleID targetModuleID) throws Exception
   {
   }

   /**
    * Stop a given module
    */
   public void stop(TargetModuleID targetModuleID) throws Exception
   {
   }

   /**
    * Undeploy a given module
    */
   public void undeploy(TargetModuleID targetModuleID)
           throws Exception
   {
      MBeanServerConnection server = getMBeanServerConnection();
      ObjectName mainDeployer = new ObjectName(MAIN_DEPLOYER);
      String url = targetModuleID.getModuleID();
      Object[] args = {url};
      String[] sig = {String.class.getName()};
      log.debug("Starting undeploy of: " + url);
      server.invoke(mainDeployer, "undeploy", args, sig);
   }

   /**
    * Retrieve the list of all J2EE application modules running or not running on the identified targets.
    */
   public TargetModuleID[] getAvailableModules(ModuleType moduleType) throws TargetException
   {
      try
      {
         List list = new ArrayList();
         MBeanServerConnection server = getMBeanServerConnection();
         ObjectName mainDeployer = new ObjectName(MAIN_DEPLOYER);
         Collection col = (Collection)server.invoke(mainDeployer, "listDeployedModules", new Object[0], new String[0]);
         for (Iterator it = col.iterator(); it.hasNext();)
         {
            SerializableDeploymentInfo info = (SerializableDeploymentInfo)it.next();
            if (info.parent == null)
            {
               String module = info.url.toString();
               TargetModuleID tmid = new TargetModuleIDImpl(this, module, null, false);
               list.add(tmid);
            }
         }

         TargetModuleID[] targetModuleIDs = new TargetModuleID[list.size()];
         list.toArray(targetModuleIDs);
         return targetModuleIDs;
      }
      catch (Exception e)
      {
         log.error("Cannot get available modules", e);
         TargetException tex = new TargetException("");
         tex.initCause(e);
         throw tex;
      }
   }

   // Get MBeanServerConnection
   private MBeanServerConnection getMBeanServerConnection()
           throws NamingException
   {
      Properties env = buildJNDIEnv();
      String lookupPath = deployURI.getPath();
      log.debug("JNDI lookup: " + lookupPath);
      log.trace("Creating InitialContext with env: " + env);
      InitialContext ctx = new InitialContext(env);
      MBeanServerConnection server = (MBeanServerConnection)ctx.lookup(lookupPath);
      return server;
   }

   private Properties buildJNDIEnv()
   {
      if (jndiEnv == null)
      {
         jndiEnv = new Properties();

         // Parse the query string for name=value pairs to put into the env
         String query = deployURI.getQuery();
         if (query != null)
         {
            log.debug("Parsing query string: " + query);
            StringTokenizer tokenizer = new StringTokenizer(query, "=&");
            while (tokenizer.hasMoreTokens())
            {
               String name = tokenizer.nextToken();
               String value = tokenizer.nextToken();
               jndiEnv.setProperty(name, value);
            }
         }

         // Set defaults for missing properties
         if (jndiEnv.getProperty(Context.INITIAL_CONTEXT_FACTORY) == null)
         {
            jndiEnv.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
         }
         if (jndiEnv.getProperty(Context.PROVIDER_URL) == null)
         {
            String host = deployURI.getHost();
            int port = deployURI.getPort();
            if (port <= 0)
               port = 1099;
            String jnpURL = "jnp://" + host + ':' + port;
            jndiEnv.setProperty(Context.PROVIDER_URL, jnpURL);
         }
         if (jndiEnv.getProperty(Context.OBJECT_FACTORIES) == null)
         {
            jndiEnv.setProperty(Context.OBJECT_FACTORIES, "org.jboss.naming");
         }
      }

      return jndiEnv;
   }
}