Package org.apache.avalon.phoenix.components.embeddor

Source Code of org.apache.avalon.phoenix.components.embeddor.DefaultEmbeddor

/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.phoenix.components.embeddor;

import java.io.File;
import java.util.Date;
import java.util.Observable;
import java.util.Observer;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;
import org.apache.avalon.excalibur.io.ExtensionFileFilter;
import org.apache.avalon.framework.CascadingException;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Initializable;
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.container.ContainerUtil;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.parameters.ParameterException;
import org.apache.avalon.framework.parameters.Parameterizable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.service.DefaultServiceManager;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.phoenix.Constants;
import org.apache.avalon.phoenix.interfaces.Deployer;
import org.apache.avalon.phoenix.interfaces.Embeddor;
import org.apache.avalon.phoenix.interfaces.EmbeddorMBean;
import org.apache.avalon.phoenix.interfaces.Kernel;
import org.apache.avalon.phoenix.interfaces.SystemManager;

/**
* This is the object that is interacted with to create, manage and
* dispose of the kernel and related resources.
*
* @author <a href="mail@leosimons.com">Leo Simons</a>
* @author <a href="peter at apache.org">Peter Donald</a>
* @author <a href="bauer@denic.de">Joerg Bauer</a>
*/
public class DefaultEmbeddor
    extends AbstractLogEnabled
    implements Embeddor, EmbeddorMBean, Contextualizable,
    Parameterizable, Configurable, Initializable, Disposable
{
    private static final Resources REZ =
        ResourceManager.getPackageResources( DefaultEmbeddor.class );

    private static final String DEFAULT_APPS_PATH = "/apps";

    private EmbeddorObservable m_observable = new EmbeddorObservable();

    private Parameters m_parameters;

    /**
     * Context passed to embeddor. See the contextualize() method
     * for details on what is stored in context.
     *
     * @see DefaultEmbeddor#contextualize(Context)
     */
    private Context m_context;

    private String m_phoenixHome;

    private EmbeddorEntry[] m_entries;

    /**
     * If true, flag indicates that the Embeddor should continue running
     * even when there are no applications in kernel. Otherwise the
     * Embeddor will shutdown when it detects there is no longer any
     * applications running.
     */
    private boolean m_persistent;

    /**
     * Flag is set to true when the embeddor should  shut itself down.
     * It is set to true as a result of a call to shutdown() method.
     *
     * @see Embeddor#shutdown()
     */
    private boolean m_shutdown;

    /**
     * Time at which the embeddor was started.
     */
    private long m_startTime;

    /**
     * The default directory in which applications are deployed from.
     */
    private String m_appDir;

    /**
     * Pass the Context to the embeddor.
     * It is expected that the following will be entries in context;
     * <ul>
     *   <li><b>common.classloader</b>: ClassLoader shared betweeen
     *      container and applications</li>
     *   <li><b>container.classloader</b>: ClassLoader used to load
     *      container</li>
     * </ul>
     *
     * @param context
     * @throws ContextException
     */
    public void contextualize( final Context context )
        throws ContextException
    {
        m_context = context;
        try
        {
            final Observer observer = (Observer)context.get( Observer.class.getName() );
            m_observable.addObserver( observer );
        }
        catch( final ContextException ce )
        {
            final String message = REZ.getString( "embeddor.notice.no-restart" );
            getLogger().warn( message );
        }
    }

    /**
     * Set parameters for this component.
     * This must be called after contextualize() and before initialize()
     *
     * Make sure to provide all the neccessary information through
     * these parameters. All information it needs consists of strings.
     * There are two types of strings included in parameters. The first
     * type include parameters used to setup proeprties of the embeddor.
     * The second type include the implementation names of the components
     * that the Embeddor manages. For instance if you want to replace the
     * <code>ConfigurationRepository</code> with your own repository you
     * would pass in a parameter such as;</p>
     * <p>org.apache.avalon.phoenix.interfaces.ConfigurationRepository =
     * com.biz.MyCustomConfigurationRepository</p>
     *
     * <p>Of the other type of parameters, the following are supported by
     * the DefaultEmbeddor implementation of Embeddor. Note that some of
     * the embedded components may support other parameters.</p>
     * <ul>
     * <li><b>phoenix.home</b>, the home directory of phoenix. Defaults
     * to "..".</li>
     * <li><b>log-destination</b>, the file to save log
     * messages in. If omitted, ${phoenix.home}/logs/phoenix.log is used.</li>
     * <li><b>log-priority</b>, the priority at which log messages are filteres.
     * If omitted, then INFO will be default level used.</li>
     * <li><b>applications-directory</b>, the directory in which
     * the defaul applications to be loaded by the kernel are stored
     * (in .sar format). Defaults to ${phoenix.home}/apps</li>
     * </ul>
     *
     * @param parameters the Parameters for embeddor
     * @throws ParameterException if an error occurs
     */
    public synchronized void parameterize( final Parameters parameters )
        throws ParameterException
    {
        m_parameters = parameters;
        m_phoenixHome = m_parameters.getParameter( "phoenix.home", ".." );
        m_persistent = m_parameters.getParameterAsBoolean( "persistent", false );
        m_appDir = m_parameters.getParameter( "phoenix.apps.dir",
                                              m_phoenixHome + DEFAULT_APPS_PATH );
    }

    public void configure( final Configuration configuration )
        throws ConfigurationException
    {
        final Configuration[] children = configuration.getChildren( "component" );
        m_entries = new EmbeddorEntry[ children.length ];
        for( int i = 0; i < children.length; i++ )
        {
            final String role = children[ i ].getAttribute( "role" );
            final String classname = children[ i ].getAttribute( "class" );
            final String logger = children[ i ].getAttribute( "logger" );
            m_entries[ i ] =
                new EmbeddorEntry( role, classname, logger, children[ i ] );
        }
    }

    /**
     * Creates the core handlers - logger, deployer, Manager and
     * Kernel. Note that these are not set up properly until you have
     * called the <code>run()</code> method.
     */
    public void initialize()
        throws Exception
    {
        m_startTime = System.currentTimeMillis();
        try
        {
            createComponents();
            setupComponents();
            registerComponents();
        }
        catch( final Exception e )
        {
            // whoops!
            final String message = REZ.getString( "embeddor.error.start.failed" );
            getLogger().fatalError( message, e );
            throw e;
        }
    }

    /**
     * This is the main method of the embeddor. It sets up the core
     * components, and then deploys the <code>Facilities</code>. These
     * are registered with the Kernel and the Manager. The same
     * happens for the <code>Applications</code>.
     * Now, the Kernel is taken through its lifecycle. When it is
     * finished, as well as all the applications running in it, it
     * is shut down, after which the PhoenixEmbeddor is as well.
     */
    public void execute()
        throws Exception
    {
        deployDefaultApplications();

        //  If the kernel is empty at this point, it is because the server was
        //  started without supplying any applications, display a message to
        //  give the user a clue as to why the server is shutting down
        //  immediately.
        if( emptyKernel() )
        {
            final String message = REZ.getString( "embeddor.error.start.no-apps" );
            getLogger().fatalError( message );
        }
        else
        {
            // loop until <code>Shutdown</code> is created.
            while( true )
            {
                // wait() for shutdown() to take action...
                if( m_shutdown
                    || (emptyKernel() && !m_persistent) )
                {
                    // The server will shut itself down when all applications are disposed.
                    if( emptyKernel() )
                    {
                        final String message =
                            REZ.getString( "embeddor.shutdown.all-apps-disposed" );
                        getLogger().info( message );
                    }
                    break;
                }
                gotoSleep();
            }
        }
    }

    private boolean emptyKernel()
    {
        final Kernel kernel = getKernel();
        if( null != kernel )
        {
            final String[] names = kernel.getApplicationNames();
            return (0 == names.length);
        }
        else
        {
            //Consider the kernel empty
            //if it has been shutdown
            return true;
        }
    }

    private void gotoSleep()
    {
        try
        {
            synchronized( this )
            {
                wait( 1000 );
            }
        }
        catch( final InterruptedException e )
        {
            //NOOP
        }
    }

    /**
     * Release all the resources associated with kernel.
     */
    public synchronized void dispose()
    {
        shutdown();
        try
        {
            unregisterComponents();
            shutdownComponents();
        }
        catch( final Exception e )
        {
            // whoops!
            final String message = REZ.getString( "embeddor.error.shutdown.failed" );
            getLogger().fatalError( message, e );
        }
        for( int i = 0; i < m_entries.length; i++ )
        {
            m_entries[ i ].setObject( null );
        }
        System.gc(); // make sure resources are released
    }

    /**
     * Request the Embeddor shutsdown.
     */
    public void shutdown()
    {
        m_shutdown = true;
        synchronized( this )
        {
            notifyAll();
        }
    }

    /**
     * Ask the embeddor to restart itself if this operation is supported.
     *
     * @throws UnsupportedOperationException if restart not supported
     */
    public void restart()
        throws UnsupportedOperationException
    {
        try
        {
            m_observable.change();
            m_observable.notifyObservers( "restart" );
        }
        catch( final Exception e )
        {
            throw new UnsupportedOperationException();
        }
    }

    /**
     * Get name by which the server is know.
     * Usually this defaults to "Phoenix" but the admin
     * may assign another name. This is useful when you
     * are managing a cluster of Phoenix servers.
     *
     * @return the name of server
     */
    public String getName()
    {
        return Constants.SOFTWARE;
    }

    /**
     * Get location of Phoenix installation
     *
     * @return the home directory of phoenix
     */
    public String getHomeDirectory()
    {
        return m_phoenixHome;
    }

    /**
     * Get the date at which this server started.
     *
     * @return the date at which this server started
     */
    public Date getStartTime()
    {
        return new Date( m_startTime );
    }

    /**
     * Retrieve the number of millisecond
     * the server has been up.
     *
     * @return the the number of millisecond the server has been up
     */
    public long getUpTimeInMillis()
    {
        return System.currentTimeMillis() - m_startTime;
    }

    /**
     * Retrieve a string identifying version of server.
     * Usually looks like "v4.0.1a".
     *
     * @return version string of server.
     */
    public String getVersion()
    {
        return Constants.VERSION;
    }

    /**
     * Get a string defining the build.
     * Possibly the date on which it was built, where it was built,
     * with what features it was built and so forth.
     *
     * @return the string describing build
     */
    public String getBuild()
    {
        return "(" + Constants.DATE + ")";
    }

    //////////////////////
    /// HELPER METHODS ///
    //////////////////////
    /**
     * Create the logger, deployer and kernel components.
     * Note that these components are not ready to be used
     * until setupComponents() is called.
     */
    private synchronized void createComponents()
  throws Exception
    {
        try
        {
            for( int i = 0; i < m_entries.length; i++ )
            {
                final String className = m_entries[ i ].getClassName();
                final Class clazz = Class.forName( className );
                final Object object = createObject( className, clazz );
                m_entries[ i ].setObject( object );
            }
        }
        catch( final Exception e )
        {
            final String message =
                REZ.getString( "embeddor.error.createComponents.failed" );
            getLogger().fatalError( message, e );
            throw new CascadingException( message, e );
        }
    }

    /**
     * The deployer is used to load the applications from the
     * default-apps-location specified in Parameters.
     * TODO: load facilities from .fars as well.
     *
     * @throws Exception if an error occurs
     */
    protected void deployDefaultApplications()
        throws Exception
    {
        //Name of optional application specified on CLI
        final String application =
            m_parameters.getParameter( "application-location", null );
        if( null != application )
        {
            final File file = new File( application );
            deployFile( file );
        }
        if( null != m_appDir )
        {
            final File directory = new File( m_appDir );
            final ExtensionFileFilter filter = new ExtensionFileFilter( ".sar" );
            final File[] files = directory.listFiles( filter );
            if( null != files )
            {
                deployFiles( files );
            }
        }
    }

    private void deployFiles( final File[] files )
        throws Exception
    {
        for( int i = 0; i < files.length; i++ )
        {
            deployFile( files[ i ] );
        }
    }

    private void deployFile( final File file )
        throws Exception
    {
        final String filename = file.getName();
        int index = filename.lastIndexOf( '.' );
        if( -1 == index )
        {
            index = filename.length();
        }
        final String name = filename.substring( 0, index );
        final File canonicalFile = file.getCanonicalFile();
        deployFile( name, canonicalFile );
    }

    protected final synchronized void deployFile( final String name, final File file )
        throws Exception
    {
        final Deployer deployer = (Deployer)getEmbeddorComponent( Deployer.ROLE );
        deployer.deploy( name, file.toURL() );
    }

    private void setupComponents()
        throws Exception
    {
        for( int i = 0; i < m_entries.length; i++ )
        {
            final EmbeddorEntry entry = m_entries[ i ];
            setupComponent( entry.getObject(),
                            entry.getLoggerName(),
                            entry.getConfiguration() );
        }
    }

    /**
     * Setup a component and run it through al of it's
     * setup lifecycle stages.
     *
     * @param object the component
     * @throws Exception if an error occurs
     */
    private void setupComponent( final Object object,
                                 final String loggerName,
                                 final Configuration config )
        throws Exception
    {
        final Logger childLogger = getLogger().getChildLogger( loggerName );
        ContainerUtil.enableLogging( object, childLogger );
        ContainerUtil.contextualize( object, m_context );
        ContainerUtil.service( object, getServiceManager() );
        ContainerUtil.parameterize( object, createChildParameters() );
        ContainerUtil.configure( object, config );
        ContainerUtil.initialize( object );
        ContainerUtil.start( object );
    }

    private Parameters createChildParameters()
    {
        final Parameters parameters = new Parameters();
        parameters.merge( m_parameters );
        parameters.setParameter( "phoenix.apps.dir", m_appDir );
        return parameters;
    }

    private void shutdownComponents()
        throws Exception
    {
        //for( int i = m_entries.length - 1; i >= 0; i-- )
        for( int i = 0; i < m_entries.length; i++ )
        {
            final Object object = m_entries[ i ].getObject();
            if( null == object )
            {
                continue;
            }
            ContainerUtil.shutdown( object );
        }
    }

    /**
     * Create a component that implements an interface.
     *
     * @param classname the name of the objects class
     * @param service the name of interface/type
     * @return the created object
     * @throws Exception if an error occurs
     */
    private Object createObject( final String classname,
                                 final Class service )
        throws Exception
    {
        try
        {
            final Object object = Class.forName( classname ).newInstance();
            if( !service.isInstance( object ) )
            {
                final String message =
                    REZ.getString( "bad-type.error",
                                   classname,
                                   service.getName() );
                throw new Exception( message );
            }
            return object;
        }
        catch( final IllegalAccessException iae )
        {
            final String message = REZ.getString( "bad-ctor.error", service.getName(), classname );
            throw new CascadingException( message, iae );
        }
        catch( final InstantiationException ie )
        {
            final String message =
                REZ.getString( "no-instantiate.error",
                               service.getName(),
                               classname );
            throw new CascadingException( message, ie );
        }
        catch( final ClassNotFoundException cnfe )
        {
            final String message = REZ.getString( "no-class.error", service.getName(), classname );
            throw new CascadingException( message, cnfe );
        }
    }

    /**
     * Register embeddor and it's components to <code>SystemManager</code>.
     */
    private void registerComponents()
        throws Exception
    {
        final SystemManager systemManager =
            (SystemManager)getServiceManager().lookup( SystemManager.ROLE );

        final SystemManager componentManager =
            systemManager.getSubContext( null, "component" );

        componentManager.register( ManagementRegistration.EMBEDDOR.getName(),
                                   this,
                                   ManagementRegistration.EMBEDDOR.getInterfaces() );

        for( int i = 0; i < m_entries.length; i++ )
        {
            final ManagementRegistration registration =
                ManagementRegistration.getManagementInfoForRole( m_entries[ i ].getRole() );
            if( null != registration )
            {
                componentManager.register( registration.getName(),
                                           m_entries[ i ].getObject(),
                                           registration.getInterfaces() );
            }
        }
    }

    /**
     * Unregister embeddor and it's components from <code>SystemManager</code>.
     */
    private void unregisterComponents()
        throws Exception
    {
        final SystemManager systemManager =
            (SystemManager)getServiceManager().lookup( SystemManager.ROLE );

        final SystemManager componentManager = systemManager.getSubContext( null, "component" );

        componentManager.unregister( ManagementRegistration.EMBEDDOR.getName() );

        for( int i = 0; i < m_entries.length; i++ )
        {
            final ManagementRegistration registration =
                ManagementRegistration.getManagementInfoForRole( m_entries[ i ].getRole() );
            if( null != registration )
            {
                componentManager.unregister( registration.getName() );
            }
        }
    }

    private ServiceManager getServiceManager()
    {
        final DefaultServiceManager serviceManager = new DefaultServiceManager();
        serviceManager.put( Embeddor.ROLE, this );
        for( int i = 0; i < m_entries.length; i++ )
        {
            final String role = m_entries[ i ].getRole();
            final Object component = getEmbeddorComponent( role );
            serviceManager.put( role, component );
        }
        return serviceManager;
    }

    /**
     * Allow subclasses to get access to kernel.
     *
     * @return the Kernel
     */
    protected final Kernel getKernel()
    {
        return (Kernel)getEmbeddorComponent( Kernel.ROLE );
    }

    /**
     * Allow subclasses to get access to parameters.
     *
     * @return the Parameters
     */
    protected final Parameters getParameters()
    {
        return m_parameters;
    }

    private Object getEmbeddorComponent( final String role )
    {
        for( int i = 0; i < m_entries.length; i++ )
        {
            final EmbeddorEntry entry = m_entries[ i ];
            if( entry.getRole().equals( role ) )
            {
                return m_entries[ i ].getObject();
            }
        }
        // Should never happen
        // TODO: create error / warning
        return null;
    }
}

class EmbeddorObservable
    extends Observable
{
    public void change()
    {
        super.setChanged();
    }
}
TOP

Related Classes of org.apache.avalon.phoenix.components.embeddor.DefaultEmbeddor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.