| /* |
| * 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 file. |
| */ |
| package org.apache.myrmidon.components.workspace; |
| |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import org.apache.avalon.framework.activity.Disposable; |
| import org.apache.avalon.framework.activity.Initializable; |
| import org.apache.avalon.framework.context.ContextException; |
| import org.apache.avalon.framework.component.ComponentException; |
| import org.apache.avalon.framework.component.ComponentManager; |
| import org.apache.avalon.framework.component.Composable; |
| import org.apache.avalon.framework.component.DefaultComponentManager; |
| import org.apache.avalon.framework.configuration.Configuration; |
| import org.apache.avalon.framework.logger.AbstractLoggable; |
| import org.apache.avalon.framework.parameters.ParameterException; |
| import org.apache.avalon.framework.parameters.Parameterizable; |
| import org.apache.avalon.framework.parameters.Parameters; |
| import org.apache.log.Hierarchy; |
| import org.apache.log.Logger; |
| import org.apache.log.LogTarget; |
| import org.apache.myrmidon.api.DefaultTaskContext; |
| import org.apache.myrmidon.api.TaskContext; |
| import org.apache.myrmidon.api.TaskException; |
| import org.apache.myrmidon.components.deployer.DefaultDeployer; |
| import org.apache.myrmidon.components.deployer.Deployer; |
| import org.apache.myrmidon.components.deployer.DeploymentException; |
| import org.apache.myrmidon.components.executor.DefaultExecutionFrame; |
| import org.apache.myrmidon.components.executor.ExecutionFrame; |
| import org.apache.myrmidon.components.executor.Executor; |
| import org.apache.myrmidon.components.model.Project; |
| import org.apache.myrmidon.components.model.Target; |
| import org.apache.myrmidon.components.model.TypeLib; |
| import org.apache.myrmidon.components.type.TypeManager; |
| import org.apache.myrmidon.framework.Condition; |
| import org.apache.myrmidon.listeners.ProjectListener; |
| |
| /** |
| * This is the default implementation of Workspace. |
| * |
| * @author <a href="mailto:donaldp@apache.org">Peter Donald</a> |
| */ |
| public class DefaultWorkspace |
| extends AbstractLoggable |
| implements Workspace, Composable, Parameterizable, Initializable |
| { |
| private Executor m_executor; |
| private ProjectListenerSupport m_listenerSupport = new ProjectListenerSupport(); |
| private ComponentManager m_componentManager; |
| private Parameters m_parameters; |
| private TaskContext m_baseContext; |
| private HashMap m_entrys = new HashMap(); |
| private TypeManager m_typeManager; |
| private Hierarchy m_hierarchy; |
| private int m_projectID; |
| |
| /** |
| * Add a listener to project events. |
| * |
| * @param listener the listener |
| */ |
| public void addProjectListener( final ProjectListener listener ) |
| { |
| m_listenerSupport.addProjectListener( listener ); |
| } |
| |
| /** |
| * Remove a listener from project events. |
| * |
| * @param listener the listener |
| */ |
| public void removeProjectListener( final ProjectListener listener ) |
| { |
| m_listenerSupport.removeProjectListener( listener ); |
| } |
| |
| /** |
| * Retrieve relevent services needed for engine. |
| * |
| * @param componentManager the ComponentManager |
| * @exception ComponentException if an error occurs |
| */ |
| public void compose( final ComponentManager componentManager ) |
| throws ComponentException |
| { |
| m_componentManager = componentManager; |
| m_typeManager = (TypeManager)componentManager.lookup( TypeManager.ROLE ); |
| m_executor = (Executor)componentManager.lookup( Executor.ROLE ); |
| } |
| |
| public void parameterize( final Parameters parameters ) |
| throws ParameterException |
| { |
| m_parameters = parameters; |
| } |
| |
| public void initialize() |
| throws Exception |
| { |
| m_baseContext = createBaseContext(); |
| |
| m_hierarchy = new Hierarchy(); |
| |
| final LogTarget target = new LogTargetToListenerAdapter( m_listenerSupport ); |
| m_hierarchy.setDefaultLogTarget( target ); |
| } |
| |
| /** |
| * Execute a target in a particular project. |
| * Execute in the project context. |
| * |
| * @param project the Project |
| * @param target the name of the target |
| * @exception TaskException if an error occurs |
| */ |
| public void executeProject( final Project project, final String target ) |
| throws TaskException |
| { |
| final ProjectEntry entry = getProjectEntry( project ); |
| |
| m_listenerSupport.projectStarted(); |
| |
| executeTarget( "<init>", project.getImplicitTarget(), entry.getFrame() ); |
| |
| execute( project, target, entry ); |
| |
| m_listenerSupport.projectFinished(); |
| } |
| |
| |
| private TaskContext createBaseContext() |
| throws TaskException |
| { |
| final TaskContext context = new DefaultTaskContext(); |
| |
| final String[] names = m_parameters.getNames(); |
| for( int i = 0; i < names.length; i++ ) |
| { |
| final String value = m_parameters.getParameter( names[ i ], null ); |
| context.setProperty( names[ i ], value ); |
| } |
| |
| //Add system properties so that they overide user-defined properties |
| addToContext( context, System.getProperties() ); |
| |
| return context; |
| } |
| |
| private File findTypeLib( final String libraryName ) |
| throws TaskException |
| { |
| //TODO: In future this will be expanded to allow |
| //users to specify search path or automagically |
| //add entries to lib path (like user specific or |
| //workspace specific) |
| final String name = libraryName.replace( '/', File.separatorChar ) + ".atl"; |
| |
| final String home = System.getProperty( "myrmidon.home" ); |
| final File homeDir = new File( home + File.separatorChar + "ext" ); |
| |
| final File library = new File( homeDir, name ); |
| |
| if( library.exists() ) |
| { |
| if( !library.canRead() ) |
| { |
| throw new TaskException( "Unable to read library at " + library ); |
| } |
| else |
| { |
| return library; |
| } |
| } |
| |
| throw new TaskException( "Unable to locate Type Library " + libraryName ); |
| } |
| |
| private void deployTypeLib( final Deployer deployer, final Project project ) |
| throws TaskException |
| { |
| final TypeLib[] typeLibs = project.getTypeLibs(); |
| |
| for( int i = 0; i < typeLibs.length; i++ ) |
| { |
| final TypeLib typeLib = typeLibs[ i ]; |
| final File file = findTypeLib( typeLib.getLibrary() ); |
| |
| try |
| { |
| if( null == typeLib.getRole() ) |
| { |
| deployer.deploy( file ); |
| } |
| else |
| { |
| deployer.deployType( typeLib.getRole(), typeLib.getName(), file ); |
| } |
| } |
| catch( final DeploymentException de ) |
| { |
| throw new TaskException( "Error deploying type library " + |
| typeLib + " at " + file, de ); |
| } |
| } |
| } |
| |
| private ExecutionFrame createExecutionFrame( final Project project ) |
| throws TaskException |
| { |
| final TaskContext context = new DefaultTaskContext( m_baseContext ); |
| context.setProperty( TaskContext.BASE_DIRECTORY, project.getBaseDirectory() ); |
| |
| //Create per frame ComponentManager |
| final DefaultComponentManager componentManager = |
| new DefaultComponentManager( m_componentManager ); |
| |
| //Add in child type manager so each frame can register different |
| //sets of tasks etc |
| final TypeManager typeManager = m_typeManager.createChildTypeManager(); |
| componentManager.put( TypeManager.ROLE, typeManager ); |
| |
| //We need to create a new deployer so that it deploys |
| //to project specific TypeManager |
| final DefaultDeployer deployer = new DefaultDeployer(); |
| deployer.setLogger( getLogger() ); |
| |
| try { deployer.compose( componentManager ); } |
| catch( final ComponentException ce ) |
| { |
| throw new TaskException( "Error configuring deployer", ce ); |
| } |
| |
| //HACK: Didn't call initialize because Deployer contained in Embeddor |
| // Already initialized and this would be reduendent |
| //deployer.initialize(); |
| |
| componentManager.put( Deployer.ROLE, deployer ); |
| |
| deployTypeLib( deployer, project ); |
| |
| //We need to place projects and ProjectManager |
| //in ComponentManager so as to support project-local call() |
| componentManager.put( Workspace.ROLE, this ); |
| componentManager.put( Project.ROLE, project ); |
| |
| final String[] names = project.getProjectNames(); |
| for( int i = 0; i < names.length; i++ ) |
| { |
| final String name = names[ i ]; |
| final Project other = project.getProject( name ); |
| componentManager.put( Project.ROLE + "/" + name, other ); |
| } |
| |
| final DefaultExecutionFrame frame = new DefaultExecutionFrame(); |
| |
| try |
| { |
| final Logger logger = m_hierarchy.getLoggerFor( "project" + m_projectID ); |
| m_projectID++; |
| |
| frame.setLogger( logger ); |
| frame.contextualize( context ); |
| frame.compose( componentManager ); |
| } |
| catch( final Exception e ) |
| { |
| throw new TaskException( "Error setting up ExecutionFrame", e ); |
| } |
| |
| return frame; |
| } |
| |
| private ProjectEntry getProjectEntry( final Project project ) |
| throws TaskException |
| { |
| ProjectEntry entry = (ProjectEntry)m_entrys.get( project ); |
| |
| if( null == entry ) |
| { |
| final ExecutionFrame frame = createExecutionFrame( project ); |
| entry = new ProjectEntry( project, frame ); |
| m_entrys.put( project, entry ); |
| } |
| |
| return entry; |
| } |
| |
| private Project getProject( final String name, final Project project ) |
| throws TaskException |
| { |
| final Project other = project.getProject( name ); |
| |
| if( null == other ) |
| { |
| //TODO: Fix this so location information included in description |
| throw new TaskException( "Project '" + name + "' not found." ); |
| } |
| |
| return other; |
| } |
| |
| /** |
| * Helper method to execute a target. |
| * |
| * @param project the Project |
| * @param target the name of the target |
| * @param context the context |
| * @param done the list of targets already executed in current run |
| * @exception TaskException if an error occurs |
| */ |
| private void execute( final Project project, |
| final String targetName, |
| final ProjectEntry entry ) |
| throws TaskException |
| { |
| final int index = targetName.indexOf( "->" ); |
| if( -1 != index ) |
| { |
| final String name = targetName.substring( 0, index ); |
| final String otherTargetName = targetName.substring( index + 2 ); |
| |
| final Project otherProject = getProject( name, project ); |
| final ProjectEntry otherEntry = getProjectEntry( otherProject ); |
| |
| //Execute target in referenced project |
| execute( otherProject, otherTargetName, otherEntry ); |
| return; |
| } |
| |
| final Target target = project.getTarget( targetName ); |
| if( null == target ) |
| { |
| throw new TaskException( "Unable to find target " + targetName ); |
| } |
| |
| //add target to list of targets executed |
| entry.completeTarget( targetName ); |
| |
| //execute all dependencies |
| final String[] dependencies = target.getDependencies(); |
| for( int i = 0; i < dependencies.length; i++ ) |
| { |
| if( !entry.isTargetCompleted( dependencies[ i ] ) ) |
| { |
| execute( project, dependencies[ i ], entry ); |
| } |
| } |
| |
| //notify listeners |
| m_listenerSupport.targetStarted( targetName ); |
| |
| executeTarget( targetName, target, entry.getFrame() ); |
| |
| //notify listeners |
| m_listenerSupport.targetFinished(); |
| } |
| |
| /** |
| * Method to execute a particular target instance. |
| * |
| * @param targetName the name of target |
| * @param target the target |
| * @param context the context in which to execute |
| * @exception TaskException if an error occurs |
| */ |
| private void executeTarget( final String name, |
| final Target target, |
| final ExecutionFrame frame ) |
| throws TaskException |
| { |
| //check the condition associated with target. |
| //if it is not satisfied then skip target |
| final Condition condition = target.getCondition(); |
| if( null != condition ) |
| { |
| try |
| { |
| if( false == condition.evaluate( frame.getContext() ) ) |
| { |
| getLogger().debug( "Skipping target " + name + |
| " as it does not satisfy condition" ); |
| return; |
| } |
| } |
| catch( final ContextException ce ) |
| { |
| throw new TaskException( "Error evaluating Condition for target " + |
| name, ce ); |
| } |
| } |
| |
| getLogger().debug( "Executing target " + name ); |
| |
| //frame.getContext().setProperty( Project.TARGET, target ); |
| |
| //execute all tasks assciated with target |
| final Configuration[] tasks = target.getTasks(); |
| for( int i = 0; i < tasks.length; i++ ) |
| { |
| executeTask( tasks[ i ], frame ); |
| } |
| } |
| |
| /** |
| * Execute a task. |
| * |
| * @param task the task definition |
| * @param context the context |
| * @exception TaskException if an error occurs |
| */ |
| private void executeTask( final Configuration task, final ExecutionFrame frame ) |
| throws TaskException |
| { |
| final String name = task.getName(); |
| getLogger().debug( "Executing task " + name ); |
| |
| //is setting name even necessary ??? |
| frame.getContext().setProperty( TaskContext.NAME, name ); |
| |
| //notify listeners |
| m_listenerSupport.taskStarted( name ); |
| |
| //run task |
| m_executor.execute( task, frame ); |
| |
| //notify listeners task has ended |
| m_listenerSupport.taskFinished(); |
| } |
| |
| /** |
| * Helper method to add values to a context |
| * |
| * @param context the context |
| * @param map the map of names->values |
| */ |
| private void addToContext( final TaskContext context, final Map map ) |
| throws TaskException |
| { |
| final Iterator keys = map.keySet().iterator(); |
| |
| while( keys.hasNext() ) |
| { |
| final String key = (String)keys.next(); |
| final Object value = map.get( key ); |
| context.setProperty( key, value ); |
| } |
| } |
| } |