/*
 * DeploymentManager.java
 *
 * Created on September 27, 2001, 11:36 AM
 */

package org.apache.avalon.phoenix.components.deployer;

import java.net.URL;
import java.net.MalformedURLException;
import java.io.File;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Arrays;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import org.apache.avalon.framework.logger.AbstractLoggable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.activity.Executable;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.excalibur.collections.ListUtils;
import org.apache.avalon.excalibur.io.ExtensionFileFilter;
import org.apache.avalon.excalibur.io.DirectoryFileFilter;
import org.apache.avalon.excalibur.io.OrFileFilter;
import org.apache.avalon.excalibur.monitor.Monitor;
import org.apache.avalon.excalibur.monitor.ActiveMonitor;
import org.apache.avalon.excalibur.monitor.Resource;
import org.apache.avalon.excalibur.monitor.FileResource;
import org.apache.avalon.excalibur.monitor.DirectoryResource;
import org.apache.avalon.excalibur.io.FileUtil;
import org.apache.avalon.phoenix.interfaces.Deployer;
import org.apache.avalon.phoenix.interfaces.DeploymentException;
import org.apache.avalon.phoenix.interfaces.Kernel;

/**
 * <deployments>
 *    <url>file:./apps</url>
 *    <url>http://localhost:8080/distribution</url> 
 * </deployments>
 *
 * @author  mtoma
 * @version 
 */
public class DefaultDeploymentManager extends AbstractLoggable 
    implements DeploymentManager, Configurable, Executable, Composable, 
    PropertyChangeListener     
{    
    private Deployer m_deployer;    
    private Kernel m_kernel;
    private Monitor m_monitor;
    private URL[] baskets;
    
    public void compose(ComponentManager componentManager) throws ComponentException
    {
        m_deployer = (Deployer) componentManager.lookup( Deployer.ROLE );
        m_kernel = (Kernel) componentManager.lookup( Kernel.ROLE );
        m_monitor = (Monitor) componentManager.lookup( Monitor.ROLE );
    }

    public void configure(Configuration configuration) throws ConfigurationException
    {
        try 
        {            
            final Configuration[] urls = configuration.getChildren("url");

            baskets = new URL[urls.length];
            for (int i = 0; i < urls.length; i++)
            {
                baskets[i] = new URL( urls[i].getValue() );
            }            
        } catch (MalformedURLException mue)
        {
            throw new ConfigurationException(mue.getMessage(), mue);
        }
    }
    
    public void execute() throws Exception
    {        
        for (int i = 0; i < baskets.length; i++)
        {
            monitor( baskets[i] );
            scan( baskets[i] );
        }
    }
            
    /**
     * Scans for removed files that have to be uninstalled and for new files
     * that have to be installed.
     */
    public void scan( final URL basket ) throws Exception
    {
        final File directory = FileUtil.toFile( basket );
        if ( directory.isFile() ) throw new Exception("Not a directory");

        final ExtensionFileFilter sarFilter = new ExtensionFileFilter( ".sar" );
        final DirectoryFileFilter dirFilter = new DirectoryFileFilter();
        final OrFileFilter filter = new OrFileFilter( sarFilter, dirFilter );
        
        final File[] files = directory.listFiles( filter );   
        final HashMap mapping = new HashMap();        
        
        for ( int i = 0; i < files.length; i++ )
        {
            final URL url = files[i].toURL();
            final String name = getName( url );
            mapping.put( name, url );
        }
        
        final List availableApps = new ArrayList( mapping.keySet() );        
        final List currentApps = Arrays.asList( m_kernel.getApplicationNames() );
        
        final List deployments = ListUtils.subtract( availableApps, currentApps );
        final List undeployments = ListUtils.subtract( currentApps, availableApps );
        
        for ( int i = 0; i < deployments.size(); i++ )
        {
            final String name = (String) deployments.get( i );
            final URL url = (URL) mapping.get( name );           
            m_deployer.deploy( name, url );
        }
        
        for ( int i = 0; i < undeployments.size(); i++ )
        {
            final String name = (String) deployments.get( i );            
            m_deployer.undeploy( name );
        }                       
    }
    
    /**
     * Register a URL to be monitored for changes.
     * @param URL monitored url
     */
    public void monitor( final URL url ) throws Exception
    {
        final File file = FileUtil.toFile( url );
        final Resource resource;
        
        if ( file.isFile() )
        {
            resource = new FileResource( file );
        }
        else 
        {
            resource = new DirectoryResource( file );
        }
        
        resource.addPropertyChangeListener( this );                    
        m_monitor.addResource( resource );
    }
    
    public void propertyChange( PropertyChangeEvent event ) 
    {
        try
        {
            final Resource resource = (Resource) event.getSource();
            final URL url = new File( resource.getResourceKey() ).toURL();
            
            if ( resource instanceof FileResource )
            {
                final String name = getName( url );                    
                
                m_deployer.undeploy( name );
                m_deployer.deploy( name, url );        
            }
            
            if ( resource instanceof DirectoryResource ) 
            {
                scan( url );
            }
        } 
        catch (Exception e)
        {
            getLogger().error("Unable to deploy/redeploy application", e);
        }
    }
    
    private String getName( final URL url )
    {
        final String filename = url.getFile();
        int index = filename.lastIndexOf( '.' );
        if( -1 == index ) index = filename.length();
        
        return filename.substring( 0, index );
    } 
    
}
