/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.aop.deployment;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Iterator;

import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.management.ObjectName;

import org.jboss.aop.AspectAnnotationLoader;
import org.jboss.aop.AspectManager;
import org.jboss.aop.AspectXmlLoader;
import org.jboss.deployment.DeploymentException;
import org.jboss.deployment.DeploymentInfo;
import org.jboss.deployment.SubDeployerSupport;
import org.jboss.util.file.ArchiveBrowser;
import org.jboss.util.file.ClassFileFilter;

/**
 * Deployer for Aspects
 *
 * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
 * @version $Revision: 1.11.2.10 $
 * @jmx:mbean name="jboss.aspect:AspectDeployer"
 * extends="org.jboss.deployment.SubDeployerMBean"
 */
public class AspectDeployer extends SubDeployerSupport
   implements AspectDeployerMBean
{
   /**
    * Default CTOR
    */
   public AspectDeployer()
   {
      initializeMainDeployer();
   }

   /**
    * Set the suffixes and relative order attributes.
    * 
    * Those are read at subdeployer registration time by the MainDeployer
    * to update its SuffixOrder list.
    */
   protected void initializeMainDeployer()
   {
      setSuffixes(new String[]{".aop", "-aop.xml"});
      setRelativeOrder(100);
   }

   /**
    * Returns true if this deployer can deploy the given DeploymentInfo.
    *
    * @return True if this deployer can deploy the given DeploymentInfo.
    * @jmx:managed-operation
    */
   public boolean accepts(DeploymentInfo di)
   {
      String urlStr = di.url.toString();
      return urlStr.endsWith(".aop") || urlStr.endsWith(".aop/") ||
      urlStr.endsWith("-aop.xml");
   }

   /**
    * Describe <code>init</code> method here.
    *
    * @param di a <code>DeploymentInfo</code> value
    * @throws DeploymentException if an error occurs
    * @jmx:managed-operation
    */
   public void init(DeploymentInfo di)
   throws DeploymentException
   {
      try
      {
         if (di.watch == null)
         {
            // resolve the watch
            if (di.url.getProtocol().equals("file"))
            {
               File file = new File(di.url.getFile());

               // If not directory we watch the package
               if (!file.isDirectory())
               {
                  di.watch = di.url;
               }
               // If directory we watch the xml files
               else
               {
                  di.watch = new URL(di.url, "META-INF/jboss-aop.xml");
               }
            }
            else
            {
               // We watch the top only, no directory support
               di.watch = di.url;
            }
         }
      }
      catch (Exception e)
      {
         log.error("failed to parse AOP document: ", e);
         throw new DeploymentException(e);
      }
      super.init(di);
   }

   private URL getDocUrl(DeploymentInfo di, URLClassLoader localCL) throws DeploymentException
   {
      URL docURL = di.localUrl;
      if (di.isXML == false)
         docURL = localCL.findResource("META-INF/jboss-aop.xml");
      // Validate that the descriptor was found
      if (docURL == null)
         throw new DeploymentException("Failed to find META-INF/jboss-aop.xml");
      return docURL;
   }

   /**
    * Describe <code>create</code> method here.
    *
    * @param di a <code>DeploymentInfo</code> value
    * @throws DeploymentException if an error occurs
    * @jmx:managed-operation
    */
   public void create(DeploymentInfo di)
   throws DeploymentException
   {
      try
      {
         if (!di.isXML)
         {
            Iterator it = ArchiveBrowser.getBrowser(di.url, new ClassFileFilter());
            AspectAnnotationLoader loader = new AspectAnnotationLoader(AspectManager.instance());
            loader.deployInputStreamIterator(it);

         }
         URL docURL = getDocUrl(di, di.localCl);
         AspectXmlLoader.deployXML(docURL);
         Notification msg = new Notification("AOP Deploy", this, getNextNotificationSequenceNumber());
         sendNotification(msg);
         log.info("Deployed AOP: " + di.url);
      }
      catch (Exception ex)
      {
         ex.printStackTrace();
         throw new DeploymentException(ex);
      }
   }

   /**
    * The <code>start</code> method starts all the mbeans in this DeploymentInfo..
    *
    * @param di a <code>DeploymentInfo</code> value
    * @throws DeploymentException if an error occurs
    * @jmx:managed-operation
    */
   public void start(DeploymentInfo di) throws DeploymentException
   {
   }

   /**
    * Undeploys the package at the url string specified. This will: Undeploy
    * packages depending on this one. Stop, destroy, and unregister all the
    * specified mbeans Unload this package and packages this package deployed
    * via the classpath tag. Keep track of packages depending on this one that
    * we undeployed so that they can be redeployed should this one be
    * redeployed.
    *
    * @param di the <code>DeploymentInfo</code> value to stop.
    * @jmx:managed-operation
    */
   public void stop(DeploymentInfo di)
   //throws DeploymentException
   {
      log.debug("undeploying document " + di.url);
      try
      {
         if (!di.isXML)
         {
            Iterator it = ArchiveBrowser.getBrowser(di.url, new ClassFileFilter());
            AspectAnnotationLoader loader = new AspectAnnotationLoader(AspectManager.instance());
            loader.undeployInputStreamIterator(it);

         }

         URL docURL = getDocUrl(di, di.localCl);
         //long start = System.currentTimeMillis();
         AspectXmlLoader.undeployXML(docURL);
         /*
         System.out.println("************************");
         System.out.println("undeploy took: " + (System.currentTimeMillis() - start));
         System.out.println("************************");
         */
         Notification msg = new Notification("AOP Undeploy", this, getNextNotificationSequenceNumber());
         sendNotification(msg);
      }
      catch (Exception ex)
      {
         log.error("failed to stop", ex);
      }
   }

   /**
    * Describe <code>destroy</code> method here.
    *
    * @param di a <code>DeploymentInfo</code> value
    * @jmx:managed-operation
    */
   public void destroy(DeploymentInfo di)
   //throws DeploymentException
   {
   }

   /**
    * The startService method gets the mbeanProxies for MainDeployer
    * and ServiceController, used elsewhere.
    *
    * @throws Exception if an error occurs
    */
   protected void startService() throws Exception
   {
      super.startService();
   }

   protected ObjectName getObjectName(MBeanServer server, ObjectName name)
   throws MalformedObjectNameException
   {
      return name == null ? OBJECT_NAME : name;
   }

}
