/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This 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 (at your option) any later version.
 *
 * This software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.management.j2ee.deployers;

import java.net.InetAddress;
import java.util.Set;
import javax.management.JMException;
import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.MBeanRegistration;

import org.jboss.logging.Logger;
import org.jboss.management.j2ee.J2EEDomain;
import org.jboss.management.j2ee.J2EEServer;
import org.jboss.management.j2ee.JVM;
import org.jboss.management.j2ee.factory.DefaultManagedObjectFactoryMap;
import org.jboss.management.j2ee.factory.ManagedObjectFactory;
import org.jboss.management.j2ee.factory.ManagedObjectFactoryMap;
import org.jboss.system.ServiceControllerMBean;
import org.jboss.system.ServiceMBean;

/**
 * Port of the old LocalJBossServerDomain to POJO.
 *
 * @author  <a href="mailto:andreas@jboss.org">Andreas Schaefer</a>
 * @author  <a href="mailto:scott.stark@jboss.org">Scott Stark</a>
 * @author  <a href="mailto:ales.justin@jboss.org">Ales Justin</a>
 */
public class LocalJBossServerDomain implements NotificationListener, LocalJBossServerDomainMBean, MBeanRegistration
{
   /**
    * Class logger.
    */
   private static final Logger log = Logger.getLogger(LocalJBossServerDomain.class);

   /** The mbean server */
   private MBeanServer server;

   /** The service name */
   private ObjectName serviceName;

   /**
    * The name of the JNDI service
    */
   private ObjectName jndiService;
   /**
    * The name of the JTA service
    */
   private ObjectName jtaService;
   /**
    * The name of the UserTransaction service
    */
   private ObjectName userTxService;
   /**
    * The name of the JavaMail service
    */
   private ObjectName mailService;
   /**
    * The name of the RMI_IIOP service
    */
   private ObjectName rmiiiopService;
   /**
    * The name of the service which emites URL binding events
    */
   private ObjectName jndiBindingService;

   /**
    * A mapping of JMX notifications to ManagedObjectFactory instances
    */
   private ManagedObjectFactoryMap managedObjFactoryMap;

   /**
    * The managed factory map class
    */
   private Class<?> managedObjFactoryMapClass = DefaultManagedObjectFactoryMap.class;

   /**
    * @return The JNDI service mbean name
    */
   public ObjectName getJNDIService()
   {
      return jndiService;
   }

   /**
    * @param name The JNDI service mbean name
    */
   public void setJNDIService(ObjectName name)
   {
      this.jndiService = name;
   }

   /**
    * @return The JTA service mbean name
    */
   public ObjectName getJTAService()
   {
      return jtaService;
   }

   /**
    * @param name The JTA service mbean name
    */
   public void setJTAService(ObjectName name)
   {
      this.jtaService = name;
   }

   /**
    * @return The JavaMail service mbean name
    */
   public ObjectName getMailService()
   {
      return mailService;
   }

   /**
    * @param name The JavaMail service mbean name
    */
   public void setMailService(ObjectName name)
   {
      this.mailService = name;
   }

   /**
    * @return The UserTransaction service mbean name
    */
   public ObjectName getUserTransactionService()
   {
      return userTxService;
   }

   /**
    * @param name The UserTransaction service mbean name
    */
   public void setUserTransactionService(ObjectName name)
   {
      this.userTxService = name;
   }

   /**
    * @return The RMI/IIOP service mbean name
    */
   public ObjectName getRMI_IIOPService()
   {
      return rmiiiopService;
   }

   /**
    * @param name The RMI/IIOP service mbean name
    */
   public void setRMI_IIOPService(ObjectName name)
   {
      this.rmiiiopService = name;
   }

   /**
    * @return The Jndi binding service mbean name
    */
   public ObjectName getJndiBindingService()
   {
      return jndiBindingService;
   }

   /**
    * @param name The Jndi binding service mbean name
    */
   public void setJndiBindingService(ObjectName name)
   {
      this.jndiBindingService = name;
   }

   /**
    * @return The ManagementObjFactoryMap class
    */
   public Class<?> getManagementObjFactoryMapClass()
   {
      return managedObjFactoryMapClass;
   }

   /**
    * @param cls The ManagementObjFactoryMap class
    */
   public void setManagementObjFactoryMapClass(Class<?> cls)
   {
      this.managedObjFactoryMapClass = cls;
   }

   public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception
   {
      this.server = server;
      this.serviceName = name;
      return name;
   }

   public void postRegister(Boolean registrationDone)
   {
      if (registrationDone)
      {
         try
         {
            createService();
         }
         catch (Exception e)
         {
            throw new RuntimeException(e);
         }
      }
   }

   public void preDeregister() throws Exception
   {
      destroyService();
   }

   public void postDeregister()
   {
   }

   /**
    * The JMX nofication callback. Here we create/destroy JSR77 MBeans based
    * on the create/destory notifications.
    *
    * @param msg      the notification msg
    * @param handback currently unused
    */
   public void handleNotification(Notification msg, Object handback)
   {
      if (managedObjFactoryMap == null || server == null)
      {
         return;
      }

      log.debug("handleNotification: " + msg);
      String type = msg.getType();
      Object userData = msg.getUserData();
      try
      {
         if (type.equals(ServiceMBean.CREATE_EVENT))
         {
            ManagedObjectFactory factory = managedObjFactoryMap.getFactory(msg);
            if (factory != null)
            {
               factory.create(server, userData);
            }
         }
         else if (type.equals(ServiceMBean.DESTROY_EVENT))
         {
            ManagedObjectFactory factory = managedObjFactoryMap.getFactory(msg);
            if (factory != null)
            {
               factory.destroy(server, userData);
            }
         }
      }
      catch (Throwable t)
      {
         log.debug("Failed to handle event", t);
      }
   }

   public String toString()
   {
      return "LocalJBossServerDomain { " + super.toString() + " }";
   }

   protected void createService() throws Exception
   {
      setupJ2EEMBeans();
      registerWithController();
      populateFactoryMap();
   }

   /**
    * Called to destroy the service. This unregisters with all deployers and
    * then removes all MBeans in this services domain to remove all JSR77
    * beans.
    *
    * @throws Exception for any error
    */
   protected void destroyService() throws Exception
   {
      cleanupLeftoverMBeans();
      unregisterWithController();
   }

   /**
    * Build the ManagedObjectFactoryMap used to obtain the ManagedObjectFactory
    * instances from notification msgs.
    *
    * @throws Exception for any error
    */
   private void populateFactoryMap() throws Exception
   {
      // Create the ManagedObjectFactoryMap
      managedObjFactoryMap = (ManagedObjectFactoryMap) managedObjFactoryMapClass.newInstance();
      managedObjFactoryMap.setJNDIResource(jndiService);
      managedObjFactoryMap.setJTAResource(jtaService);
      managedObjFactoryMap.setJTAResource(userTxService);
      managedObjFactoryMap.setJavaMailResource(mailService);
      managedObjFactoryMap.setRMI_IIOPResource(rmiiiopService);
   }

   /**
    * Create the J2EEServer and JVM MBeans.
    */
   private void setupJ2EEMBeans()
   {
      // Create Server Component
      try
      {
         log.debug("setupJ2EEMBeans(), create J2EEServer instance");
         Package pkg = Package.getPackage("org.jboss");
         final String vendor; 
         final String version;
         if (pkg != null)
         {
            vendor = pkg.getSpecificationVendor();
            version = pkg.getImplementationVersion();
         }
         else
         {
            vendor = "JBoss";
            version = "Unknown";
         }
         
         // Create the createService
         J2EEDomain serverDomain = new J2EEDomain(serviceName.getDomain());
         ObjectName domain = serverDomain.getObjectName();
         server.registerMBean(serverDomain, domain);
         // Create single Local J2EEServer MBean
         J2EEServer j2eeServer = new J2EEServer("Local", domain, vendor, version);
         ObjectName lServer = j2eeServer.getObjectName();
         server.registerMBean(j2eeServer, lServer);

         // Create the JVM MBean
         String hostName = "localhost";
         try
         {
            InetAddress lLocalHost = InetAddress.getLocalHost();
            hostName = lLocalHost.getHostName();
         }
         catch (Exception e)
         {
            // Ignore when host address is not accessible (localhost is used instead)
         }
         String vmVendor = System.getProperty("java.vendor");
         String vmVersion = System.getProperty("java.version");
         String name = vmVendor + " " + vmVersion;
         JVM jvm = new JVM(name, lServer, vmVersion, vmVendor, hostName);
         ObjectName jvmName = jvm.getObjectName();
         server.registerMBean(jvm, jvmName);
      }
      catch (JMException jme)
      {
         log.debug("setupJ2EEMBeans - unexpected JMException", jme);
      }
      catch (Exception e)
      {
         log.debug("setupJ2EEMBeans - unexpected exception", e);
      }
   }

   /**
    * Register as a listener of the ServiceControllerMBean
    */
   private void registerWithController()
   {
      try
      {
         server.addNotificationListener(ServiceControllerMBean.OBJECT_NAME, this, null, null);
         log.debug("Registered as listener of: " + ServiceControllerMBean.OBJECT_NAME);
      }
      catch (JMException jme)
      {
         log.debug("unexpected exception", jme);
      }
      catch (Exception e)
      {
         log.debug("unexpected exception", e);
      }
   }

   /**
    * Unregister as a listener of the ServiceControllerMBean.
    */
   private void unregisterWithController()
   {
      try
      {
         server.removeNotificationListener(ServiceControllerMBean.OBJECT_NAME, this);
         log.debug("UNRegistered as listener of: " + ServiceControllerMBean.OBJECT_NAME);
      }
      catch (JMException jme)
      {
         log.debug("unexpected exception", jme);
      }
      catch (Exception e)
      {
         log.debug("unexpected exception", e);
      }
   }

   /**
    * Query for all mbeans in this services domain and unregisters them.
    *
    * @throws Exception if the domain query fails
    */
   private void cleanupLeftoverMBeans() throws Exception
   {
      String domain = serviceName.getDomain();
      ObjectName domainName = new ObjectName(domain + ":*");
      Set domainNames = server.queryNames(domainName, null);
      log.debug("Found " + domainNames.size() + " domain mbeans");
      for (Object name : domainNames)
      {
         try
         {
            ObjectName oname = (ObjectName)name;
            if (oname.equals(serviceName) == false)
               server.unregisterMBean(oname);
         }
         catch (MBeanException ignore)
         {
         }
      }
   }
}
