You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@continuum.apache.org by ct...@apache.org on 2008/10/31 01:01:41 UTC

svn commit: r709300 [2/3] - in /continuum/trunk: continuum-api/src/main/java/org/apache/continuum/dao/ continuum-api/src/main/java/org/apache/continuum/purge/ continuum-api/src/main/java/org/apache/continuum/taskqueue/ continuum-api/src/main/java/org/a...

Modified: continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/buildcontroller/DefaultBuildController.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/buildcontroller/DefaultBuildController.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/buildcontroller/DefaultBuildController.java (original)
+++ continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/buildcontroller/DefaultBuildController.java Thu Oct 30 17:01:38 2008
@@ -22,6 +22,9 @@
 import org.apache.continuum.dao.BuildDefinitionDao;
 import org.apache.continuum.dao.BuildResultDao;
 import org.apache.continuum.dao.ProjectDao;
+import org.apache.continuum.dao.ProjectGroupDao;
+import org.apache.continuum.dao.ProjectScmRootDao;
+import org.apache.continuum.model.project.ProjectScmRoot;
 import org.apache.maven.continuum.core.action.AbstractContinuumAction;
 import org.apache.maven.continuum.execution.ContinuumBuildExecutor;
 import org.apache.maven.continuum.execution.ContinuumBuildExecutorConstants;
@@ -32,7 +35,6 @@
 import org.apache.maven.continuum.model.project.ProjectDependency;
 import org.apache.maven.continuum.model.scm.ChangeFile;
 import org.apache.maven.continuum.model.scm.ChangeSet;
-import org.apache.maven.continuum.model.scm.ScmResult;
 import org.apache.maven.continuum.notification.ContinuumNotificationDispatcher;
 import org.apache.maven.continuum.project.ContinuumProjectState;
 import org.apache.maven.continuum.store.ContinuumObjectNotFoundException;
@@ -45,7 +47,6 @@
 import org.codehaus.plexus.action.ActionNotFoundException;
 import org.codehaus.plexus.logging.AbstractLogEnabled;
 import org.codehaus.plexus.taskqueue.execution.TaskExecutionException;
-import org.codehaus.plexus.util.StringUtils;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -75,6 +76,16 @@
      * @plexus.requirement
      */
     private ProjectDao projectDao;
+    
+    /**
+     * @plexus.requirement
+     */
+    private ProjectGroupDao projectGroupDao;
+    
+    /**
+     * @plexus.requirement
+     */
+    private ProjectScmRootDao projectScmRootDao;
 
     /**
      * @plexus.requirement
@@ -112,40 +123,18 @@
         getLogger().info( "Initializing build" );
         BuildContext context = initializeBuildContext( projectId, buildDefinitionId, trigger );
 
+        // ignore this if AlwaysBuild ?
+        if ( !checkScmResult( context ) )
+        {
+            getLogger().info( "Error updating from SCM, not building" );
+            return;
+        }
+        
         getLogger().info( "Starting build of " + context.getProject().getName() );
         startBuild( context );
 
         try
         {
-            // check if build definition requires smoking the existing checkout and rechecking out project
-            if ( context.getBuildDefinition().isBuildFresh() )
-            {
-                getLogger().info( "Purging exiting working copy" );
-                cleanWorkingDirectory( context );
-            }
-
-            // ----------------------------------------------------------------------
-            // TODO: Centralize the error handling from the SCM related actions.
-            // ContinuumScmResult should return a ContinuumScmResult from all
-            // methods, even in a case of failure.
-            // ----------------------------------------------------------------------
-            getLogger().info( "Updating working dir" );
-            updateWorkingDirectory( context );
-
-            getLogger().info( "Merging SCM results" );
-            //CONTINUUM-1393
-            if ( !context.getBuildDefinition().isBuildFresh() )
-            {
-                mergeScmResults( context );
-            }
-
-            // ignore this if AlwaysBuild ?
-            if ( !checkScmResult( context ) )
-            {
-                getLogger().info( "Error updating from SCM, not building" );
-                return;
-            }
-
             checkProjectDependencies( context );
 
             if ( !shouldBuild( context ) )
@@ -171,9 +160,11 @@
 
             performAction( "deploy-artifact", context );
 
+            context.setCancelled( (Boolean) actionContext.get( AbstractContinuumAction.KEY_CANCELLED ) );
+
             String s = (String) actionContext.get( AbstractContinuumAction.KEY_BUILD_ID );
 
-            if ( s != null )
+            if ( s != null && !context.isCancelled() )
             {
                 try
                 {
@@ -215,7 +206,7 @@
             if ( project.getState() != ContinuumProjectState.NEW &&
                 project.getState() != ContinuumProjectState.CHECKEDOUT &&
                 project.getState() != ContinuumProjectState.OK && project.getState() != ContinuumProjectState.FAILED &&
-                project.getState() != ContinuumProjectState.ERROR )
+                project.getState() != ContinuumProjectState.ERROR && !context.isCancelled() )
             {
                 try
                 {
@@ -241,7 +232,10 @@
         }
         finally
         {
-            notifierDispatcher.buildComplete( project, context.getBuildDefinition(), context.getBuildResult() );
+            if ( !context.isCancelled() )
+            {
+                notifierDispatcher.buildComplete( project, context.getBuildDefinition(), context.getBuildResult() );
+            }
         }
     }
 
@@ -288,11 +282,6 @@
 
     private void updateBuildResult( BuildResult build, BuildContext context )
     {
-        if ( build.getScmResult() == null && context.getScmResult() != null )
-        {
-            build.setScmResult( context.getScmResult() );
-        }
-
         if ( build.getModifiedDependencies() == null && context.getModifiedDependencies() != null )
         {
             build.setModifiedDependencies( context.getModifiedDependencies() );
@@ -342,7 +331,9 @@
 
         try
         {
-            context.setProject( projectDao.getProject( projectId ) );
+            Project project = projectDao.getProjectWithScmDetails( projectId );
+
+            context.setProject( project );
 
             BuildDefinition buildDefinition = buildDefinitionDao.getBuildDefinition( buildDefinitionId );
 
@@ -353,14 +344,16 @@
 
             context.setOldBuildResult( oldBuildResult );
 
+		    context.setScmResult( project.getScmResult() );
+		    
             // CONTINUUM-1871 olamy if continuum is killed during building oldBuildResult will have a endTime 0
             // this means all changes since the project has been loaded in continuum will be in memory
             // now we will load all BuildResult with an Id bigger or equals than the oldBuildResult one
-            if ( oldBuildResult != null )
-            {
-                context.setOldScmResult(
-                    getOldScmResults( projectId, oldBuildResult.getBuildNumber(), oldBuildResult.getEndTime() ) );
-            }
+            //if ( oldBuildResult != null )
+            //{
+            //    context.setOldScmResult(
+            //        getOldScmResults( projectId, oldBuildResult.getBuildNumber(), oldBuildResult.getEndTime() ) );
+            //}
         }
         catch ( ContinuumStoreException e )
         {
@@ -381,46 +374,12 @@
 
         actionContext.put( AbstractContinuumAction.KEY_FIRST_RUN, context.getOldBuildResult() == null );
 
-        return context;
-    }
-
-    private void cleanWorkingDirectory( BuildContext context )
-        throws TaskExecutionException
-    {
-        performAction( "clean-working-directory", context );
-    }
-
-    private void updateWorkingDirectory( BuildContext context )
-        throws TaskExecutionException
-    {
-        Map actionContext = context.getActionContext();
-
-        performAction( "check-working-directory", context );
-
-        boolean workingDirectoryExists =
-            AbstractContinuumAction.getBoolean( actionContext, AbstractContinuumAction.KEY_WORKING_DIRECTORY_EXISTS );
-
-        ScmResult scmResult;
-
-        if ( workingDirectoryExists )
+        if ( context.getOldBuildResult() != null )
         {
-            performAction( "update-working-directory-from-scm", context );
-
-            scmResult = AbstractContinuumAction.getUpdateScmResult( actionContext, null );
+            actionContext.put( AbstractContinuumAction.KEY_OLD_BUILD_ID, context.getOldBuildResult().getId() );
         }
-        else
-        {
-            Project project = (Project) actionContext.get( AbstractContinuumAction.KEY_PROJECT );
-
-            actionContext.put( AbstractContinuumAction.KEY_WORKING_DIRECTORY,
-                               workingDirectoryService.getWorkingDirectory( project ).getAbsolutePath() );
-
-            performAction( "checkout-project", context );
 
-            scmResult = AbstractContinuumAction.getCheckoutResult( actionContext, null );
-        }
-
-        context.setScmResult( scmResult );
+        return context;
     }
 
     private void performAction( String actionName, BuildContext context )
@@ -695,48 +654,6 @@
         }
     }
 
-    private String convertScmResultToError( ScmResult result )
-    {
-        String error = "";
-
-        if ( result == null )
-        {
-            error = "Scm result is null.";
-        }
-        else
-        {
-            if ( result.getCommandLine() != null )
-            {
-                error = "Command line: " + StringUtils.clean( result.getCommandLine() ) +
-                    System.getProperty( "line.separator" );
-            }
-
-            if ( result.getProviderMessage() != null )
-            {
-                error = "Provider message: " + StringUtils.clean( result.getProviderMessage() ) +
-                    System.getProperty( "line.separator" );
-            }
-
-            if ( result.getCommandOutput() != null )
-            {
-                error += "Command output: " + System.getProperty( "line.separator" );
-                error += "-------------------------------------------------------------------------------" +
-                    System.getProperty( "line.separator" );
-                error += StringUtils.clean( result.getCommandOutput() ) + System.getProperty( "line.separator" );
-                error += "-------------------------------------------------------------------------------" +
-                    System.getProperty( "line.separator" );
-            }
-
-            if ( result.getException() != null )
-            {
-                error += "Exception:" + System.getProperty( "line.separator" );
-                error += result.getException();
-            }
-        }
-
-        return error;
-    }
-
     // ----------------------------------------------------------------------
     //
     // ----------------------------------------------------------------------
@@ -759,8 +676,6 @@
 
         updateBuildResult( build, context );
 
-        build.setScmResult( context.getScmResult() );
-
         build.setBuildDefinition( context.getBuildDefinition() );
 
         if ( error != null )
@@ -784,79 +699,6 @@
         }
     }
 
-    private ScmResult getOldScmResults( int projectId, long startId, long fromDate )
-        throws ContinuumStoreException
-    {
-        List<BuildResult> results = buildResultDao.getBuildResultsForProjectFromId( projectId, startId );
-
-        ScmResult res = new ScmResult();
-
-        if ( results != null )
-        {
-            for ( BuildResult result : results )
-            {
-                ScmResult scmResult = result.getScmResult();
-
-                if ( scmResult != null )
-                {
-                    List<ChangeSet> changes = scmResult.getChanges();
-
-                    if ( changes != null )
-                    {
-                        for ( ChangeSet changeSet : changes )
-                        {
-                            if ( changeSet.getDate() < fromDate )
-                            {
-                                continue;
-                            }
-                            if ( !res.getChanges().contains( changeSet ) )
-                            {
-                                res.addChange( changeSet );
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        return res;
-    }
-
-    /**
-     * Merges scm results so we'll have all changes since last execution of current build definition
-     *
-     * @param context The build context
-     */
-    private void mergeScmResults( BuildContext context )
-    {
-        ScmResult oldScmResult = context.getOldScmResult();
-        ScmResult newScmResult = context.getScmResult();
-
-        if ( oldScmResult != null )
-        {
-            if ( newScmResult == null )
-            {
-                context.setScmResult( oldScmResult );
-            }
-            else
-            {
-                List<ChangeSet> oldChanges = oldScmResult.getChanges();
-
-                List<ChangeSet> newChanges = newScmResult.getChanges();
-
-                for ( ChangeSet change : newChanges )
-                {
-                    if ( !oldChanges.contains( change ) )
-                    {
-                        oldChanges.add( change );
-                    }
-                }
-
-                newScmResult.setChanges( oldChanges );
-            }
-        }
-    }
-
     /**
      * Check to see if there was a error while checking out/updating the project
      *
@@ -867,34 +709,26 @@
     private boolean checkScmResult( BuildContext context )
         throws TaskExecutionException
     {
-        ScmResult scmResult = context.getScmResult();
+        Project project = context.getProject();
 
-        if ( scmResult == null || !scmResult.isSuccess() )
-        {
-            // scmResult must be converted before storing it because jpox modifies values of all fields to null
-            String error = convertScmResultToError( scmResult );
+        int projectGroupId = project.getProjectGroup().getId();
 
-            BuildResult build = makeAndStoreBuildResult( context, error );
+        List<ProjectScmRoot> scmRoots = projectScmRootDao.getProjectScmRootByProjectGroup( projectGroupId );
 
-            try
+        for ( ProjectScmRoot projectScmRoot : scmRoots )
+        {
+            if ( project.getScmUrl().startsWith( projectScmRoot.getScmRootAddress() ) )
             {
-                Project project = context.getProject();
-
-                project.setState( build.getState() );
-
-                projectDao.updateProject( project );
+                if ( projectScmRoot.getState() == ContinuumProjectState.UPDATED )
+                {
+                    return true;
+                }
 
-                return false;
-            }
-            catch ( ContinuumStoreException e )
-            {
-                throw new TaskExecutionException( "Error storing project", e );
+                break;
             }
         }
 
-        context.getActionContext().put( AbstractContinuumAction.KEY_UPDATE_SCM_RESULT, scmResult );
-
-        return true;
+        return false;
     }
 
 }

Modified: continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/AbstractContinuumAction.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/AbstractContinuumAction.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/AbstractContinuumAction.java (original)
+++ continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/AbstractContinuumAction.java Thu Oct 30 17:01:38 2008
@@ -19,6 +19,7 @@
  * under the License.
  */
 
+import org.apache.continuum.model.project.ProjectScmRoot;
 import org.apache.maven.continuum.model.project.BuildDefinition;
 import org.apache.maven.continuum.model.project.BuildDefinitionTemplate;
 import org.apache.maven.continuum.model.project.Project;
@@ -82,6 +83,16 @@
     
     public static final String KEY_SCM_PASSWORD = "scmUserPassword";
 
+    public static final String KEY_SCM_RESULT = "scmResult";
+
+    public static final String KEY_OLD_SCM_RESULT = "old-scmResult";
+
+    public static final String KEY_PROJECT_SCM_ROOT = "projectScmRoot";
+
+    public static final String KEY_OLD_BUILD_ID = "old-buildResult-id";
+
+    public static final String KEY_CANCELLED = "cancelled";
+
     // ----------------------------------------------------------------------
     // Utils
     // ----------------------------------------------------------------------
@@ -180,6 +191,36 @@
         return (List) getObject( context, KEY_UPDATE_DEPENDENCIES, defaultValue );
     }
 
+    public static ScmResult getScmResult( Map context )
+    {
+        return (ScmResult) getObject( context, KEY_SCM_RESULT );
+    }
+
+    public static ScmResult getScmResult( Map context, ScmResult defaultValue )
+    {
+        return (ScmResult) getObject( context, KEY_SCM_RESULT, defaultValue );
+    }
+
+    public static ScmResult getOldScmResult( Map context )
+    {
+        return (ScmResult) getObject( context, KEY_OLD_SCM_RESULT );
+    }
+
+    public static ScmResult getOldScmResult( Map context, ScmResult defaultValue )
+    {
+        return (ScmResult) getObject( context, KEY_OLD_SCM_RESULT, defaultValue );
+    }
+
+    public static ProjectScmRoot getProjectScmRoot( Map context )
+    {
+        return (ProjectScmRoot) getObject( context, KEY_PROJECT_SCM_ROOT );
+    }
+
+    public static int getOldBuildId( Map context )
+    {
+        return getInteger( context, KEY_OLD_BUILD_ID ); 
+    }
+
     // ----------------------------------------------------------------------
     //
     // ----------------------------------------------------------------------
@@ -206,7 +247,16 @@
 
     protected static int getInteger( Map context, String key )
     {
-        return ( (Integer) getObject( context, key, null ) ).intValue();
+        Object obj = getObject( context, key, null );
+        
+        if ( obj == null )
+        {
+            return 0;
+        }
+        else
+        {
+            return ( (Integer) obj ).intValue();
+        }
     }
 
     protected static Object getObject( Map context, String key )

Modified: continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/AddProjectToCheckOutQueueAction.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/AddProjectToCheckOutQueueAction.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/AddProjectToCheckOutQueueAction.java (original)
+++ continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/AddProjectToCheckOutQueueAction.java Thu Oct 30 17:01:38 2008
@@ -20,10 +20,10 @@
  */
 
 import org.apache.continuum.dao.ProjectDao;
+import org.apache.continuum.taskqueue.manager.TaskQueueManager;
 import org.apache.maven.continuum.model.project.Project;
 import org.apache.maven.continuum.scm.queue.CheckOutTask;
 import org.apache.maven.continuum.utils.WorkingDirectoryService;
-import org.codehaus.plexus.taskqueue.TaskQueue;
 
 import java.util.Map;
 
@@ -42,15 +42,15 @@
     private WorkingDirectoryService workingDirectoryService;
 
     /**
-     * @plexus.requirement role-hint="check-out-project"
+     * @plexus.requirement
      */
-    private TaskQueue checkOutQueue;
+    private ProjectDao projectDao;
 
     /**
      * @plexus.requirement
      */
-    private ProjectDao projectDao;
-
+    private TaskQueueManager taskQueueManager;
+    
     @SuppressWarnings("unchecked")
     public void execute( Map context )
         throws Exception
@@ -65,6 +65,6 @@
         CheckOutTask checkOutTask = new CheckOutTask( project.getId(), workingDirectoryService
             .getWorkingDirectory( project ), project.getName(), project.getScmUsername(), project.getScmPassword() );
 
-        checkOutQueue.put( checkOutTask );
+        taskQueueManager.getCheckoutQueue().put( checkOutTask );
     }
 }

Modified: continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/CheckoutProjectContinuumAction.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/CheckoutProjectContinuumAction.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/CheckoutProjectContinuumAction.java (original)
+++ continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/CheckoutProjectContinuumAction.java Thu Oct 30 17:01:38 2008
@@ -193,6 +193,7 @@
         }
 
         context.put( KEY_CHECKOUT_SCM_RESULT, result );
+        context.put( KEY_PROJECT, project );
     }
 
     private ContinuumScmConfiguration createScmConfiguration( Project project, File workingDirectory,

Modified: continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/CreateProjectsFromMetadataAction.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/CreateProjectsFromMetadataAction.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/CreateProjectsFromMetadataAction.java (original)
+++ continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/CreateProjectsFromMetadataAction.java Thu Oct 30 17:01:38 2008
@@ -23,11 +23,13 @@
 import java.net.MalformedURLException;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.maven.continuum.ContinuumException;
 import org.apache.maven.continuum.execution.maven.m2.SettingsConfigurationException;
 import org.apache.maven.continuum.model.project.BuildDefinitionTemplate;
+import org.apache.maven.continuum.model.project.Project;
 import org.apache.maven.continuum.project.builder.ContinuumProjectBuilder;
 import org.apache.maven.continuum.project.builder.ContinuumProjectBuilderException;
 import org.apache.maven.continuum.project.builder.ContinuumProjectBuildingResult;
@@ -38,6 +40,7 @@
 import org.apache.maven.settings.MavenSettingsBuilder;
 import org.apache.maven.settings.Server;
 import org.apache.maven.settings.Settings;
+import org.codehaus.plexus.util.StringUtils;
 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
 
 /**
@@ -154,7 +157,11 @@
                     result.addError( ContinuumProjectBuildingResult.ERROR_MALFORMED_URL );
                 }
             }
-
+     
+            if ( result.getProjects() != null )
+            {
+                context.put( KEY_URL, getScmRootUrl( result.getProjects() ) );
+            }
         }
         catch ( MalformedURLException e )
         {
@@ -206,6 +213,32 @@
         }
     }
 
+    private String getScmRootUrl( List<Project> projects )
+    {
+        String scmRootUrl = "";
+        
+        for ( Project project : projects )
+        {
+            String scmUrl = project.getScmUrl();
+            
+            scmRootUrl = getCommonPath( scmUrl, scmRootUrl );
+        }
+        
+        return scmRootUrl;
+    }
+
+    private String getCommonPath( String path1, String path2 )
+    {
+        if ( path2.equals( "" ) )
+        {
+            return path1;
+        }
+        else
+        {
+            int indexDiff = StringUtils.differenceAt( path1, path2 );
+            return path1.substring( 0, indexDiff );
+        }
+    }
 
     public ContinuumProjectBuilderManager getProjectBuilderManager()
     {

Modified: continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/ExecuteBuilderContinuumAction.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/ExecuteBuilderContinuumAction.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/ExecuteBuilderContinuumAction.java (original)
+++ continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/ExecuteBuilderContinuumAction.java Thu Oct 30 17:01:38 2008
@@ -22,13 +22,14 @@
 import org.apache.continuum.dao.BuildResultDao;
 import org.apache.continuum.dao.ProjectDao;
 import org.apache.maven.continuum.configuration.ConfigurationService;
+import org.apache.maven.continuum.execution.ContinuumBuildCancelledException;
 import org.apache.maven.continuum.execution.ContinuumBuildExecutionResult;
 import org.apache.maven.continuum.execution.ContinuumBuildExecutor;
 import org.apache.maven.continuum.execution.manager.BuildExecutorManager;
 import org.apache.maven.continuum.model.project.BuildDefinition;
 import org.apache.maven.continuum.model.project.BuildResult;
 import org.apache.maven.continuum.model.project.Project;
-import org.apache.maven.continuum.model.scm.ScmResult;
+//import org.apache.maven.continuum.model.scm.ScmResult;
 import org.apache.maven.continuum.notification.ContinuumNotificationDispatcher;
 import org.apache.maven.continuum.project.ContinuumProjectState;
 import org.apache.maven.continuum.utils.ContinuumUtils;
@@ -85,8 +86,6 @@
 
         int trigger = getTrigger( context );
 
-        ScmResult scmResult = getUpdateScmResult( context );
-
         List updatedDependencies = getUpdatedDependencies( context );
 
         ContinuumBuildExecutor buildExecutor = buildExecutorManager.getBuildExecutor( project.getExecutorId() );
@@ -103,8 +102,6 @@
 
         buildResult.setTrigger( trigger );
 
-        buildResult.setScmResult( scmResult );
-
         buildResult.setModifiedDependencies( updatedDependencies );
 
         buildResult.setBuildDefinition( getBuildDefinition( context ) );
@@ -113,6 +110,8 @@
 
         context.put( KEY_BUILD_ID, Integer.toString( buildResult.getId() ) );
 
+        context.put( KEY_CANCELLED, new Boolean( false ) );
+
         buildResult = buildResultDao.getBuildResult( buildResult.getId() );
 
         try
@@ -127,6 +126,14 @@
 
             buildResult.setExitCode( result.getExitCode() );
         }
+        catch ( ContinuumBuildCancelledException e )
+        {
+            getLogger().info( "Cancelled build" );
+            
+            buildResult.setState( ContinuumProjectState.CANCELLED );
+            
+            context.put( KEY_CANCELLED, new Boolean( true ) );
+        }
         catch ( Throwable e )
         {
             getLogger().error( "Error running buildResult", e );
@@ -137,42 +144,57 @@
         }
         finally
         {
-            buildResult.setEndTime( new Date().getTime() );
-
             project = projectDao.getProject( project.getId() );
 
-            if ( buildResult.getState() == ContinuumProjectState.OK )
+            if ( buildResult.getState() == ContinuumProjectState.CANCELLED )
             {
-                project.setBuildNumber( project.getBuildNumber() + 1 );
-            }
-
-            project.setLatestBuildId( buildResult.getId() );
+                project.setState( project.getOldState() );
 
-            buildResult.setBuildNumber( project.getBuildNumber() );
+                project.setOldState( 0 );
 
-            if ( buildResult.getState() != ContinuumProjectState.OK &&
-                buildResult.getState() != ContinuumProjectState.FAILED &&
-                buildResult.getState() != ContinuumProjectState.ERROR )
-            {
-                buildResult.setState( ContinuumProjectState.ERROR );
-            }
+                int buildResultId = getOldBuildId( context ); 
 
-            project.setState( buildResult.getState() );
+                project.setLatestBuildId( buildResultId );
 
-            // ----------------------------------------------------------------------
-            // Copy over the buildResult result
-            // ----------------------------------------------------------------------
+                buildResultDao.removeBuildResult( buildResult );
+            }
+            else
+            {
+                buildResult.setEndTime( new Date().getTime() );
 
-            buildResultDao.updateBuildResult( buildResult );
+                if ( buildResult.getState() == ContinuumProjectState.OK )
+                {
+                    project.setBuildNumber( project.getBuildNumber() + 1 );
+                }
+
+                project.setLatestBuildId( buildResult.getId() );
+
+                buildResult.setBuildNumber( project.getBuildNumber() );
+
+                if ( buildResult.getState() != ContinuumProjectState.OK &&
+                    buildResult.getState() != ContinuumProjectState.FAILED &&
+                    buildResult.getState() != ContinuumProjectState.ERROR )
+                {
+                    buildResult.setState( ContinuumProjectState.ERROR );
+                }
+
+                project.setState( buildResult.getState() );
+
+                // ----------------------------------------------------------------------
+                // Copy over the buildResult result
+                // ----------------------------------------------------------------------
+    
+                buildResultDao.updateBuildResult( buildResult );
+    
+                buildResult = buildResultDao.getBuildResult( buildResult.getId() );
 
-            buildResult = buildResultDao.getBuildResult( buildResult.getId() );
+                notifier.goalsCompleted( project, buildDefinition, buildResult );
+            }
 
             context.put( KEY_PROJECT, project );
 
             projectDao.updateProject( project );
 
-            notifier.goalsCompleted( project, buildDefinition, buildResult );
-
             // ----------------------------------------------------------------------
             // Backup test result files
             // ----------------------------------------------------------------------

Modified: continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/UpdateWorkingDirectoryFromScmContinuumAction.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/UpdateWorkingDirectoryFromScmContinuumAction.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/UpdateWorkingDirectoryFromScmContinuumAction.java (original)
+++ continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/core/action/UpdateWorkingDirectoryFromScmContinuumAction.java Thu Oct 30 17:01:38 2008
@@ -33,6 +33,7 @@
 import org.apache.maven.continuum.project.ContinuumProjectState;
 import org.apache.maven.continuum.store.ContinuumObjectNotFoundException;
 import org.apache.maven.continuum.store.ContinuumStoreException;
+import org.apache.maven.continuum.utils.ContinuumUtils;
 import org.apache.maven.continuum.utils.WorkingDirectoryService;
 import org.apache.maven.scm.ScmException;
 import org.apache.maven.scm.ScmFile;
@@ -42,6 +43,7 @@
 
 import java.io.File;
 import java.util.Date;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -94,12 +96,15 @@
 
         UpdateScmResult scmResult;
 
+        ScmResult result;
+
         Date latestUpdateDate = null;
+
         try
         {
-            BuildResult result = buildResultDao.getLatestBuildResultForProject( project.getId() );
+            BuildResult buildResult = buildResultDao.getLatestBuildResultForProject( project.getId() );
 
-            latestUpdateDate = new Date( result.getStartTime() );
+            latestUpdateDate = new Date( buildResult.getStartTime() );
         }
         catch ( Exception e )
         {
@@ -133,7 +138,38 @@
                 getLogger().info( "Updated " + scmResult.getUpdatedFiles().size() + " files." );
             }
 
-            context.put( KEY_UPDATE_SCM_RESULT, convertScmResult( scmResult ) );
+            result = convertScmResult( scmResult );
+        }
+        catch ( ScmRepositoryException e )
+        {
+            result = new ScmResult();
+
+            result.setSuccess( false );
+
+            result.setProviderMessage( e.getMessage() + ": " + getValidationMessages( e ) );
+            
+            getLogger().error( e.getMessage(), e);
+        }
+        catch ( NoSuchScmProviderException e )
+        {
+            // TODO: this is not making it back into a result of any kind - log it at least. Same is probably the case for ScmException
+            result = new ScmResult();
+
+            result.setSuccess( false );
+
+            result.setProviderMessage( e.getMessage() );
+            
+            getLogger().error( e.getMessage(), e);
+        }
+        catch ( ScmException e )
+        {
+            result = new ScmResult();
+
+            result.setSuccess( false );
+
+            result.setException( ContinuumUtils.throwableMessagesToString( e ) );
+            
+            getLogger().error( e.getMessage(), e);
         }
         finally
         {
@@ -155,6 +191,9 @@
 
             notifier.checkoutComplete( project, buildDefinition );
         }
+        
+        context.put( KEY_UPDATE_SCM_RESULT, result );
+        context.put( KEY_PROJECT, project );
     }
 
     private ContinuumScmConfiguration createScmConfiguration( Project project, File workingDirectory )
@@ -275,4 +314,25 @@
 
         return cmd;
     }
+    
+    private String getValidationMessages( ScmRepositoryException ex )
+    {
+        List<String> messages = ex.getValidationMessages();
+
+        StringBuffer message = new StringBuffer();
+
+        if ( messages != null && !messages.isEmpty() )
+        {
+            for ( Iterator<String> i = messages.iterator(); i.hasNext(); )
+            {
+                message.append( i.next() );
+
+                if ( i.hasNext() )
+                {
+                    message.append( System.getProperty( "line.separator" ) );
+                }
+            }
+        }
+        return message.toString();
+    }
 }

Modified: continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/execution/maven/m2/DefaultMavenBuilderHelper.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/execution/maven/m2/DefaultMavenBuilderHelper.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/execution/maven/m2/DefaultMavenBuilderHelper.java (original)
+++ continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/execution/maven/m2/DefaultMavenBuilderHelper.java Thu Oct 30 17:01:38 2008
@@ -343,6 +343,8 @@
 
                     userNotifier.setSendOnWarning( notifier.isSendOnWarning() );
 
+                    userNotifier.setSendOnScmFailure( notifier.isSendOnScmFailure() );
+
                     userNotifiers.add( userNotifier );
                 }
             }
@@ -545,6 +547,8 @@
 
                 notifier.setSendOnWarning( projectNotifier.isSendOnWarning() );
 
+                notifier.setSendOnScmFailure( false );
+
                 notifiers.add( notifier );
             }
         }

Modified: continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/notification/DefaultContinuumNotificationDispatcher.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/notification/DefaultContinuumNotificationDispatcher.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/notification/DefaultContinuumNotificationDispatcher.java (original)
+++ continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/notification/DefaultContinuumNotificationDispatcher.java Thu Oct 30 17:01:38 2008
@@ -21,6 +21,7 @@
 
 import org.apache.continuum.dao.ProjectDao;
 import org.apache.continuum.dao.ProjectGroupDao;
+import org.apache.continuum.model.project.ProjectScmRoot;
 import org.apache.maven.continuum.model.project.BuildDefinition;
 import org.apache.maven.continuum.model.project.BuildResult;
 import org.apache.maven.continuum.model.project.Project;
@@ -96,6 +97,11 @@
         sendNotification( MESSAGE_ID_BUILD_COMPLETE, project, buildDefinition, buildResult );
     }
 
+    public void prepareBuildComplete( ProjectScmRoot projectScmRoot )
+    {
+        sendNotification( MESSAGE_ID_PREPARE_BUILD_COMPLETE, projectScmRoot );
+    }
+
     // ----------------------------------------------------------------------
     //
     // ----------------------------------------------------------------------
@@ -117,56 +123,17 @@
             // Here we need to get all the project details
             //  - builds are used to detect if the state has changed (TODO: maybe previousState field is better)
             //  - notifiers are used to send the notification
+            //  - scm results are used to detect if scm failed
             project = projectDao.getProjectWithAllDetails( project.getId() );
 
             ProjectGroup projectGroup =
                 projectGroupDao.getProjectGroupWithBuildDetailsByProjectGroupId( project.getProjectGroup().getId() );
 
             Map<String, List<ProjectNotifier>> notifiersMap = new HashMap<String, List<ProjectNotifier>>();
-            // perform the project level notifications
-            for ( ProjectNotifier notifier : (List<ProjectNotifier>) project.getNotifiers() )
-            {
-                List<ProjectNotifier> notifiers = notifiersMap.get( notifier.getType() );
-                if ( notifiers == null )
-                {
-                    notifiers = new ArrayList<ProjectNotifier>();
-                }
-
-                if ( !notifier.isEnabled() )
-                {
-                    log.info( notifier.getType() + " notifier (id=" + notifier.getId() + ") is disabled." );
-
-                    continue;
-                }
-
-                notifiers.add( notifier );
-                notifiersMap.put( notifier.getType(), notifiers );
-            }
-
-            // perform the project group level notifications
-            if ( projectGroup.getNotifiers() != null )
-            {
-                for ( ProjectNotifier projectNotifier : (List<ProjectNotifier>) projectGroup.getNotifiers() )
-                {
-                    List<ProjectNotifier> projectNotifiers = notifiersMap.get( projectNotifier.getType() );
-                    if ( projectNotifiers == null )
-                    {
-                        projectNotifiers = new ArrayList<ProjectNotifier>();
-                    }
-
-                    if ( !projectNotifier.isEnabled() )
-                    {
-                        log.info( projectNotifier.getType() + " projectNotifier (id=" + projectNotifier.getId() +
-                            ") is disabled." );
-
-                        continue;
-                    }
-
-                    projectNotifiers.add( projectNotifier );
-                    notifiersMap.put( projectNotifier.getType(), projectNotifiers );
-                }
-            }
-
+            
+            getProjectNotifiers( project, notifiersMap );
+            getProjectGroupNotifiers( projectGroup, notifiersMap );
+            
             for ( String notifierType : notifiersMap.keySet() )
             {
                 MessageContext context = new MessageContext();
@@ -190,6 +157,34 @@
         }
     }
 
+    private void sendNotification( String messageId, ProjectScmRoot projectScmRoot )
+    {
+        try
+        {
+            ProjectGroup group =
+                projectGroupDao.getProjectGroupWithBuildDetailsByProjectGroupId( projectScmRoot.getProjectGroup().getId() );
+            
+            Map<String, List<ProjectNotifier>> notifiersMap = new HashMap<String, List<ProjectNotifier>>();
+            getProjectGroupNotifiers( group, notifiersMap );
+            
+            for ( String notifierType : notifiersMap.keySet() )
+            {
+                MessageContext context = new MessageContext();
+                context.setProjectScmRoot( projectScmRoot );
+
+                List<ProjectNotifier> projectNotifiers = notifiersMap.get( notifierType );
+                context.setNotifier( projectNotifiers );
+
+                sendNotification( messageId, context );
+            }
+
+        }
+        catch ( ContinuumStoreException e )
+        {
+            log.error( "Error while population the notification context.", e );
+        }
+    }
+
     private void sendNotification( String messageId, MessageContext context )
     {
         String notifierType = context.getNotifiers().get( 0 ).getType();
@@ -205,4 +200,57 @@
             log.error( "Error while trying to use the " + notifierType + " notifier.", e );
         }
     }
+    
+    private void getProjectNotifiers( Project project, Map<String, List<ProjectNotifier>> notifiersMap )
+    {
+        if ( project.getNotifiers() != null )
+        {
+            // perform the project level notifications
+            for ( ProjectNotifier notifier : (List<ProjectNotifier>) project.getNotifiers() )
+            {
+                List<ProjectNotifier> notifiers = notifiersMap.get( notifier.getType() );
+                if ( notifiers == null )
+                {
+                    notifiers = new ArrayList<ProjectNotifier>();
+                }
+
+                if ( !notifier.isEnabled() )
+                {
+                    log.info( notifier.getType() + " notifier (id=" + notifier.getId() + ") is disabled." );
+
+                    continue;
+                }
+
+                notifiers.add( notifier );
+                notifiersMap.put( notifier.getType(), notifiers );
+            }
+        }
+    }
+    
+    private void getProjectGroupNotifiers( ProjectGroup projectGroup, Map<String, List<ProjectNotifier>>  notifiersMap )
+    {
+        // perform the project group level notifications
+        if ( projectGroup.getNotifiers() != null )
+        {
+            for ( ProjectNotifier projectNotifier : (List<ProjectNotifier>) projectGroup.getNotifiers() )
+            {
+                List<ProjectNotifier> projectNotifiers = notifiersMap.get( projectNotifier.getType() );
+                if ( projectNotifiers == null )
+                {
+                    projectNotifiers = new ArrayList<ProjectNotifier>();
+                }
+
+                if ( !projectNotifier.isEnabled() )
+                {
+                    log.info( projectNotifier.getType() + " projectNotifier (id=" + projectNotifier.getId() +
+                        ") is disabled." );
+
+                    continue;
+                }
+
+                projectNotifiers.add( projectNotifier );
+                notifiersMap.put( projectNotifier.getType(), projectNotifiers );
+            }
+        }
+    }
 }

Modified: continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/notification/console/ConsoleNotifier.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/notification/console/ConsoleNotifier.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/notification/console/ConsoleNotifier.java (original)
+++ continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/notification/console/ConsoleNotifier.java Thu Oct 30 17:01:38 2008
@@ -19,12 +19,14 @@
  * under the License.
  */
 
+import org.apache.continuum.model.project.ProjectScmRoot;
 import org.apache.maven.continuum.model.project.BuildResult;
 import org.apache.maven.continuum.model.project.Project;
 import org.apache.maven.continuum.notification.AbstractContinuumNotifier;
 import org.apache.maven.continuum.notification.ContinuumNotificationDispatcher;
 import org.apache.maven.continuum.notification.MessageContext;
 import org.apache.maven.continuum.notification.NotificationException;
+import org.apache.maven.continuum.project.ContinuumProjectState;
 import org.codehaus.plexus.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -56,6 +58,8 @@
 
         BuildResult build = context.getBuildResult();
 
+        ProjectScmRoot projectScmRoot = context.getProjectScmRoot();
+
         if ( messageId.equals( ContinuumNotificationDispatcher.MESSAGE_ID_BUILD_STARTED ) )
         {
             buildStarted( project );
@@ -80,6 +84,10 @@
         {
             buildComplete( project, build );
         }
+        else if ( messageId.equals( ContinuumNotificationDispatcher.MESSAGE_ID_PREPARE_BUILD_COMPLETE ) )
+        {
+            prepareBuildComplete( projectScmRoot );
+        }
         else
         {
             log.warn( "Unknown messageId: '" + messageId + "'." );
@@ -134,6 +142,23 @@
         }
     }
 
+    private void prepareBuildStarted( ProjectScmRoot projectScmRoot )
+    {
+        out( projectScmRoot, "Prepare build started." );
+    }
+
+    private void prepareBuildComplete( ProjectScmRoot projectScmRoot )
+    {
+        if ( StringUtils.isEmpty( projectScmRoot.getError() ) )
+        {
+            out( projectScmRoot, "Prepare build complete. state: " + projectScmRoot.getState() );
+        }
+        else
+        {
+            out( projectScmRoot, "Prepare build complete." );
+        }
+    }
+
     private void out( Project project, BuildResult build, String msg )
     {
         System.out.println( "Build event for project '" + project.getName() + "':" + msg );
@@ -143,4 +168,17 @@
             System.out.println( build.getError() );
         }
     }
+
+    private void out( ProjectScmRoot projectScmRoot, String msg )
+    {
+        if ( projectScmRoot != null )
+        {
+            System.out.println( "Prepare build event for '" + projectScmRoot.getScmRootAddress() + "':" + msg );
+
+            if ( !StringUtils.isEmpty( projectScmRoot.getError() ) )
+            {
+                System.out.println( projectScmRoot.getError() );
+            }
+        }
+    }
 }

Modified: continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/notification/mail/MailContinuumNotifier.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/notification/mail/MailContinuumNotifier.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/notification/mail/MailContinuumNotifier.java (original)
+++ continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/notification/mail/MailContinuumNotifier.java Thu Oct 30 17:01:38 2008
@@ -19,6 +19,7 @@
  * under the License.
  */
 
+import org.apache.continuum.model.project.ProjectScmRoot;
 import org.apache.maven.continuum.Continuum;
 import org.apache.maven.continuum.configuration.ConfigurationService;
 import org.apache.maven.continuum.execution.ExecutorConfigurator;
@@ -31,6 +32,7 @@
 import org.apache.maven.continuum.model.project.BuildResult;
 import org.apache.maven.continuum.model.project.Project;
 import org.apache.maven.continuum.model.project.ProjectDeveloper;
+import org.apache.maven.continuum.model.project.ProjectGroup;
 import org.apache.maven.continuum.model.project.ProjectNotifier;
 import org.apache.maven.continuum.model.scm.ChangeSet;
 import org.apache.maven.continuum.model.scm.ScmResult;
@@ -247,12 +249,21 @@
         BuildResult build = context.getBuildResult();
         String buildOutput = getBuildOutput( project, build );
         BuildDefinition buildDefinition = context.getBuildDefinition();
-
+        ProjectScmRoot projectScmRoot = context.getProjectScmRoot();
+        
+        boolean isPrepareBuildComplete =
+            messageId.equals( ContinuumNotificationDispatcher.MESSAGE_ID_PREPARE_BUILD_COMPLETE );
+        
+        if ( projectScmRoot == null && isPrepareBuildComplete )
+        {
+            return;
+        }
+        
         // ----------------------------------------------------------------------
         // If there wasn't any building done, don't notify
         // ----------------------------------------------------------------------
 
-        if ( build == null )
+        if ( build == null && !isPrepareBuildComplete )
         {
             return;
         }
@@ -265,6 +276,10 @@
         {
             buildComplete( project, notifiers, build, buildOutput, messageId, context, buildDefinition );
         }
+        else if ( isPrepareBuildComplete )
+        {
+            prepareBuildComplete( projectScmRoot, notifiers, messageId, context );
+        }
     }
 
     private void buildComplete( Project project, List<ProjectNotifier> notifiers, BuildResult build, String buildOutput,
@@ -329,8 +344,7 @@
 
                 context.put( "project", project );
 
-                context.put( "changesSinceLastSuccess", continuum.getChangesSinceLastSuccess( project.getId(), build
-                    .getId() ) );
+                context.put( "changesSinceLastUpdate", continuum.getChangesSinceLastUpdate( project.getId() ) );
 
                 context.put( "previousBuild", previousBuild );
 
@@ -408,6 +422,75 @@
         sendMessage( project, notifiers, subject, content, messageContext );
     }
 
+    private void prepareBuildComplete( ProjectScmRoot projectScmRoot, List<ProjectNotifier> notifiers, 
+                                       String messageId, MessageContext messageContext )
+        throws NotificationException
+    {
+        // ----------------------------------------------------------------------
+        // Generate the mail contents
+        // ----------------------------------------------------------------------
+
+        String packageName = getClass().getPackage().getName().replace( '.', '/' );
+
+        String templateName = packageName + "/templates/"  + messageId + ".vm";
+
+        StringWriter writer = new StringWriter();
+
+        String content;
+
+        try
+        {
+            VelocityContext context = new VelocityContext();
+            
+            // ----------------------------------------------------------------------
+            // Data objects
+            // ----------------------------------------------------------------------
+
+            context.put( "reportUrl", getReportUrl( projectScmRoot.getProjectGroup(), 
+                                                    projectScmRoot, configurationService ) );
+
+            context.put( "projectGroup", projectScmRoot.getProjectGroup() );
+
+            context.put( "projectScmRoot", projectScmRoot );
+            
+            // TODO put other profile env var could be a security if they provide passwords ?
+
+            // ----------------------------------------------------------------------
+            // Generate
+            // ----------------------------------------------------------------------
+
+            velocity.getEngine().mergeTemplate( templateName, context, writer );
+
+            content = writer.getBuffer().toString();
+        }
+        catch ( ResourceNotFoundException e )
+        {
+            log.info( "No such template: '" + templateName + "'." );
+
+            return;
+        }
+        catch ( Exception e )
+        {
+            throw new NotificationException( "Error while generating mail contents.", e );
+        }
+        
+        // ----------------------------------------------------------------------
+        // Send the mail
+        // ----------------------------------------------------------------------
+
+        String subject;
+        try
+        {
+            subject = generateSubject( projectScmRoot );
+        }
+        catch ( Exception e )
+        {
+            throw new NotificationException( "Error while generating mail subject.", e );
+        }
+
+        sendMessage( projectScmRoot, notifiers, subject, content, messageContext );
+    }
+    
     // ----------------------------------------------------------------------
     //
     // ----------------------------------------------------------------------
@@ -489,6 +572,23 @@
 
         return writer.toString();
     }
+    
+    private String generateSubject( ProjectScmRoot projectScmRoot )
+        throws Exception
+    {
+        String state = getState( projectScmRoot );
+        subjectFormat = "[continuum] PREPARE BUILD ${state}: ${projectScmRoot.scmRootAddress}";
+        
+        VelocityContext context = new VelocityContext();
+        context.put( "projectScmRoot", projectScmRoot );
+        context.put( "state", state );
+    
+        StringWriter writer = new StringWriter();
+    
+        boolean velocityResults = velocity.getEngine().evaluate( context, writer, "subjectPattern", subjectFormat );
+    
+        return writer.toString();
+    }
 
     private String getState( Project project, BuildResult build )
     {
@@ -518,7 +618,28 @@
             return "ERROR: Unknown build state " + state;
         }
     }
+    
+    private String getState( ProjectScmRoot projectScmRoot )
+    {
+        int state = projectScmRoot.getState();
 
+        if ( state == ContinuumProjectState.UPDATED )
+        {
+            return "SUCCESSFUL";
+        }
+        else if ( state == ContinuumProjectState.ERROR )
+        {
+            return "ERROR";
+        }
+        else
+        {
+            log.warn( "Unknown prepare build state " + state + " for SCM Root URL " + projectScmRoot.getScmRootAddress() +
+                      " in projectGroup " + projectScmRoot.getProjectGroup().getId() );
+
+            return "ERROR: Unknown build state " + state;
+        }
+    }
+    
     private void sendMessage( Project project, List<ProjectNotifier> notifiers, String subject, String content,
                               MessageContext context )
         throws NotificationException
@@ -595,7 +716,7 @@
                         {
                             if ( Boolean.parseBoolean( committerField ) )
                             {
-                                ScmResult scmResult = context.getBuildResult().getScmResult();
+                                ScmResult scmResult = context.getProject().getScmResult();
                                 if ( scmResult != null && scmResult.getChanges() != null &&
                                     !scmResult.getChanges().isEmpty() )
                                 {
@@ -652,7 +773,107 @@
             message.setSentDate( new Date() );
 
             javaMailSender.send( message );
-            //mailSender.send( message );
+        }
+        catch ( AddressException ex )
+        {
+            throw new NotificationException( "Exception while sending message.", ex );
+        }
+        catch ( MessagingException ex )
+        {
+            throw new NotificationException( "Exception while sending message.", ex );
+        }
+        catch ( UnsupportedEncodingException ex )
+        {
+            throw new NotificationException( "Exception while sending message.", ex );
+        }
+    }
+
+    private void sendMessage( ProjectScmRoot projectScmRoot, List<ProjectNotifier> notifiers, 
+                              String subject, String content, MessageContext context )
+        throws NotificationException
+    {
+        ProjectGroup projectGroup = projectScmRoot.getProjectGroup();
+        
+        if ( notifiers.size() == 0 )
+        {
+            // This is a useful message for the users when debugging why they don't
+            // receive any mails
+
+            log.info( "No mail notifier for '" + projectGroup.getName() + "'." );
+
+            return;
+        }
+
+        String fromMailbox = getFromMailbox( notifiers );
+
+        if ( fromMailbox == null )
+        {
+            log
+                .warn( projectGroup.getName() +
+                    ": ProjectGroup is missing nag email and global from mailbox is missing, not sending mail." );
+
+            return;
+        }
+
+        MimeMessage message = javaMailSender.createMimeMessage();
+        
+        try
+        {
+            message.setSubject( subject );
+
+            log.info( "Message Subject: '" + subject + "'." );
+
+            message.setText( content );
+
+            InternetAddress from = new InternetAddress( fromMailbox, fromName );
+
+            message.setFrom( from );
+
+            log.info( "Sending message: From '" + from + "'." );
+            
+            if ( StringUtils.isEmpty( toOverride ) )
+            {
+                for ( ProjectNotifier notifier : notifiers )
+                {
+                    if ( !shouldNotify( projectScmRoot, notifier ) )
+                    {
+                        continue;
+                    }
+                    
+                    Map<String, String> conf = notifier.getConfiguration();
+                    if ( conf != null )
+                    {
+                        String addressField = conf.get( ADDRESS_FIELD );
+
+                        if ( StringUtils.isNotEmpty( addressField ) )
+                        {
+                            String[] addresses = StringUtils.split( addressField, "," );
+
+                            for ( String address : addresses )
+                            {
+                                // TODO: set a proper name
+                                InternetAddress to = new InternetAddress( address.trim() );
+
+                                log.info( "Recipient: To '" + to + "'." );
+                                message.addRecipient( Message.RecipientType.TO, to );
+                            }
+                        }
+                    }
+                }
+            }
+            else
+            {
+                // TODO: use configuration file instead of to load it fron component configuration
+                // TODO: set a proper name
+                InternetAddress to = new InternetAddress( toOverride.trim() );
+                log.info( "Recipient: To '" + to + "'." );
+
+                message.addRecipient( Message.RecipientType.TO, to );
+            }
+
+            message.setSentDate( new Date() );
+
+            javaMailSender.send( message );
         }
         catch ( AddressException ex )
         {

Modified: continuum/trunk/continuum-core/src/main/resources/META-INF/plexus/components.xml
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/main/resources/META-INF/plexus/components.xml?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/main/resources/META-INF/plexus/components.xml (original)
+++ continuum/trunk/continuum-core/src/main/resources/META-INF/plexus/components.xml Thu Oct 30 17:01:38 2008
@@ -60,6 +60,9 @@
         <requirement>
           <role>org.apache.maven.continuum.buildcontroller.BuildController</role>
         </requirement>
+        <requirement>
+          <role>org.apache.continuum.taskqueue.manager.TaskQueueManager</role>
+        </requirement>
       </requirements>
     </component>
 
@@ -118,5 +121,39 @@
     </component>
 
 
+    <!--
+     |
+     | Prepare Build Project Task Queue
+     |
+     |-->
+     
+    <component>
+      <role>org.codehaus.plexus.taskqueue.TaskQueue</role>
+      <role-hint>prepare-build-project</role-hint>
+      <implementation>org.codehaus.plexus.taskqueue.DefaultTaskQueue</implementation>
+      <lifecycle-handler>plexus-configurable</lifecycle-handler>
+    </component>
+    
+    <component>
+      <role>org.codehaus.plexus.taskqueue.execution.TaskQueueExecutor</role>
+      <role-hint>prepare-build-project</role-hint>
+      <implementation>org.codehaus.plexus.taskqueue.execution.ThreadedTaskQueueExecutor</implementation>
+      <instantiation-strategy>singleton</instantiation-strategy>
+      <requirements>
+        <requirement>
+          <role>org.codehaus.plexus.taskqueue.execution.TaskExecutor</role>
+          <role-hint>prepare-build-project</role-hint>
+        </requirement>
+        <requirement>
+          <role>org.codehaus.plexus.taskqueue.TaskQueue</role>
+          <role-hint>prepare-build-project</role-hint>
+        </requirement>
+      </requirements>
+      <configuration>
+        <name>prepare-build-project</name>
+      </configuration>
+    </component>
+    
+    
   </components>
 </component-set>

Modified: continuum/trunk/continuum-core/src/test/java/org/apache/continuum/purge/DefaultContinuumPurgeManagerTest.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/test/java/org/apache/continuum/purge/DefaultContinuumPurgeManagerTest.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/test/java/org/apache/continuum/purge/DefaultContinuumPurgeManagerTest.java (original)
+++ continuum/trunk/continuum-core/src/test/java/org/apache/continuum/purge/DefaultContinuumPurgeManagerTest.java Thu Oct 30 17:01:38 2008
@@ -26,6 +26,7 @@
 import org.apache.continuum.model.repository.LocalRepository;
 import org.apache.continuum.model.repository.RepositoryPurgeConfiguration;
 import org.apache.continuum.purge.task.PurgeTask;
+import org.apache.continuum.taskqueue.manager.TaskQueueManager;
 import org.apache.maven.continuum.AbstractContinuumTest;
 import org.codehaus.plexus.taskqueue.Task;
 import org.codehaus.plexus.taskqueue.TaskQueue;
@@ -52,6 +53,8 @@
 
     private DirectoryPurgeConfiguration dirPurge;
 
+    private TaskQueueManager taskQueueManager;
+
     @Override
     protected void setUp()
         throws Exception
@@ -70,6 +73,8 @@
 
         purgeQueue = (TaskQueue) lookup( TaskQueue.ROLE, "purge" );
 
+        taskQueueManager = (TaskQueueManager) lookup( TaskQueueManager.ROLE );
+
         setupDefaultPurgeConfigurations();
     }
 
@@ -124,14 +129,14 @@
 
         purgeManager.purgeRepository( repoPurge );
         purgeManager.purgeDirectory( dirPurge );
-        purgeManager.removeFromPurgeQueue( repoPurge.getId() );
+        taskQueueManager.removeFromPurgeQueue( repoPurge.getId() );
 
         assertNextBuildIs( dirPurge.getId() );
         assertNextBuildIsNull();
 
         purgeManager.purgeRepository( repoPurge );
         purgeManager.purgeDirectory( dirPurge );
-        purgeManager.removeFromPurgeQueue( dirPurge.getId() );
+        taskQueueManager.removeFromPurgeQueue( dirPurge.getId() );
 
         assertNextBuildIs( repoPurge.getId() );
         assertNextBuildIsNull();

Modified: continuum/trunk/continuum-core/src/test/java/org/apache/maven/continuum/DefaultContinuumTest.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/test/java/org/apache/maven/continuum/DefaultContinuumTest.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/test/java/org/apache/maven/continuum/DefaultContinuumTest.java (original)
+++ continuum/trunk/continuum-core/src/test/java/org/apache/maven/continuum/DefaultContinuumTest.java Thu Oct 30 17:01:38 2008
@@ -29,6 +29,7 @@
 import org.apache.continuum.model.release.ContinuumReleaseResult;
 import org.apache.continuum.model.repository.LocalRepository;
 import org.apache.continuum.repository.RepositoryService;
+import org.apache.continuum.taskqueue.manager.TaskQueueManager;
 import org.apache.maven.continuum.builddefinition.BuildDefinitionService;
 import org.apache.maven.continuum.configuration.ConfigurationService;
 import org.apache.maven.continuum.execution.ContinuumBuildExecutorConstants;
@@ -351,6 +352,8 @@
     {
         Continuum continuum = (Continuum) lookup( Continuum.ROLE );
 
+        TaskQueueManager taskQueueManager = (TaskQueueManager) lookup( TaskQueueManager.ROLE );
+
         String url = getTestFile( "src/test-projects/project1/pom.xml" ).toURL().toExternalForm();
 
         ContinuumProjectBuildingResult result = continuum.addMavenTwoProject( url );
@@ -366,10 +369,10 @@
         Project project = (Project) projects.get( 0 );
 
         assertTrue( "project missing from the checkout queue",
-                    continuum.removeProjectFromCheckoutQueue( project.getId() ) );
+                    taskQueueManager.removeProjectFromCheckoutQueue( project.getId() ) );
 
         assertFalse( "project still exist on the checkout queue",
-                     continuum.removeProjectFromCheckoutQueue( project.getId() ) );
+                     taskQueueManager.removeProjectFromCheckoutQueue( project.getId() ) );
     }
 
     public void testAddAntProjectWithdefaultBuildDef()

Modified: continuum/trunk/continuum-core/src/test/java/org/apache/maven/continuum/notification/mail/MailContinuumNotifierTest.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/test/java/org/apache/maven/continuum/notification/mail/MailContinuumNotifierTest.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/test/java/org/apache/maven/continuum/notification/mail/MailContinuumNotifierTest.java (original)
+++ continuum/trunk/continuum-core/src/test/java/org/apache/maven/continuum/notification/mail/MailContinuumNotifierTest.java Thu Oct 30 17:01:38 2008
@@ -33,6 +33,7 @@
 import org.apache.maven.continuum.AbstractContinuumTest;
 import org.apache.maven.continuum.model.project.BuildResult;
 import org.apache.maven.continuum.model.project.Project;
+import org.apache.maven.continuum.model.project.ProjectGroup;
 import org.apache.maven.continuum.model.project.ProjectNotifier;
 import org.apache.maven.continuum.notification.ContinuumNotificationDispatcher;
 import org.apache.maven.continuum.notification.MessageContext;
@@ -59,8 +60,9 @@
         String toOverride = "recipient@host.com";
         notifier.setToOverride( toOverride );
 
-        Project project = makeStubProject( "Test Project" );
-        project.setGroupId( "foo.bar" );
+        ProjectGroup group = createStubProjectGroup( "foo.bar", "" );
+
+        Project project = addProject( "Test Project", group );
 
         BuildResult build = makeBuild( ContinuumProjectState.OK );
 
@@ -74,8 +76,9 @@
     public void testFailedBuild()
         throws Exception
     {
-        Project project = makeStubProject( "Test Project" );
-        project.setGroupId( "foo.bar" );
+        ProjectGroup group = createStubProjectGroup( "foo.bar", "" );
+
+        Project project = addProject( "Test Project", group );
 
         BuildResult build = makeBuild( ContinuumProjectState.FAILED );
 
@@ -89,8 +92,9 @@
     public void testErrorenousBuild()
         throws Exception
     {
-        Project project = makeStubProject( "Test Project" );
-        project.setGroupId( "foo.bar" );
+        ProjectGroup group = createStubProjectGroup( "foo.bar", "" );
+
+        Project project = addProject( "Test Project", group );
 
         BuildResult build = makeBuild( ContinuumProjectState.ERROR );
 

Modified: continuum/trunk/continuum-data-management/data-management-jdo/src/main/java/org/apache/maven/continuum/management/JdoDataManagementTool.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-data-management/data-management-jdo/src/main/java/org/apache/maven/continuum/management/JdoDataManagementTool.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-data-management/data-management-jdo/src/main/java/org/apache/maven/continuum/management/JdoDataManagementTool.java (original)
+++ continuum/trunk/continuum-data-management/data-management-jdo/src/main/java/org/apache/maven/continuum/management/JdoDataManagementTool.java Thu Oct 30 17:01:38 2008
@@ -6,11 +6,11 @@
 import org.apache.continuum.dao.LocalRepositoryDao;
 import org.apache.continuum.dao.ProfileDao;
 import org.apache.continuum.dao.ProjectGroupDao;
+import org.apache.continuum.dao.ProjectScmRootDao;
 import org.apache.continuum.dao.RepositoryPurgeConfigurationDao;
 import org.apache.continuum.dao.ScheduleDao;
 import org.apache.continuum.dao.SystemConfigurationDao;
 import org.apache.continuum.model.repository.LocalRepository;
-import org.apache.continuum.model.repository.LocalRepository;
 import org.apache.maven.continuum.model.project.BuildDefinition;
 import org.apache.maven.continuum.model.project.ContinuumDatabase;
 import org.apache.maven.continuum.model.project.Project;
@@ -97,6 +97,11 @@
      */
     private SystemConfigurationDao systemConfigurationDao;
 
+    /**
+     * @plexus.requirement
+     */
+    private ProjectScmRootDao projectScmRootDao;
+
     protected static final String BUILDS_XML = "builds.xml";
 
     /**
@@ -136,6 +141,8 @@
             repositoryPurgeConfigurationDao.getAllRepositoryPurgeConfigurations() );
         database.setDirectoryPurgeConfigurations( directoryPurgeConfigurationDao.getAllDirectoryPurgeConfigurations() );
 
+        database.setProjectScmRoots( projectScmRootDao.getAllProjectScmRoots() );
+
         ContinuumStaxWriter writer = new ContinuumStaxWriter();
 
         File backupFile = new File( backupDirectory, BUILDS_XML );
@@ -239,6 +246,7 @@
             localRepositories.put( Integer.valueOf( localRepository.getId() ), localRepository );
         }
 
+        Map<Integer, ProjectGroup> projectGroups = new HashMap<Integer, ProjectGroup>();
         for ( Iterator i = database.getProjectGroups().iterator(); i.hasNext(); )
         {
             ProjectGroup projectGroup = (ProjectGroup) i.next();
@@ -259,13 +267,8 @@
                                                  Integer.valueOf( projectGroup.getLocalRepository().getId() ) ) );
             }
 
-            if ( projectGroup.getLocalRepository() != null )
-            {
-                projectGroup.setLocalRepository(
-                    localRepositories.get( Integer.valueOf( projectGroup.getLocalRepository().getId() ) ) );
-            }
-
-            PlexusJdoUtils.addObject( pmf.getPersistenceManager(), projectGroup );
+            projectGroup = (ProjectGroup) PlexusJdoUtils.addObject( pmf.getPersistenceManager(), projectGroup );
+            projectGroups.put( Integer.valueOf( projectGroup.getId() ), projectGroup );
         }
     }
 

Modified: continuum/trunk/continuum-data-management/data-management-jdo/src/test/resources/expected.xml
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-data-management/data-management-jdo/src/test/resources/expected.xml?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-data-management/data-management-jdo/src/test/resources/expected.xml (original)
+++ continuum/trunk/continuum-data-management/data-management-jdo/src/test/resources/expected.xml Thu Oct 30 17:01:38 2008
@@ -540,4 +540,13 @@
       <schedule id="1"></schedule>
     </directoryPurgeConfiguration>
   </directoryPurgeConfigurations>
+  <projectScmRoots>
+    <projectScmRoot>
+      <id>1</id>
+      <scmRootAddress>scmRootAddress1</scmRootAddress>
+      <state>1</state>
+      <error>error1</error>
+      <projectGroup id="2"></projectGroup>
+    </projectScmRoot>
+  </projectScmRoots>
 </continuumDatabase>

Modified: continuum/trunk/continuum-model/pom.xml
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-model/pom.xml?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-model/pom.xml (original)
+++ continuum/trunk/continuum-model/pom.xml Thu Oct 30 17:01:38 2008
@@ -63,7 +63,7 @@
           </execution>
         </executions>
         <configuration>
-          <version>1.1.2</version>
+          <version>1.1.3</version>
           <packageWithVersion>false</packageWithVersion>
           <model>src/main/mdo/continuum.xml</model>
         </configuration>

Modified: continuum/trunk/continuum-model/src/main/mdo/continuum.xml
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-model/src/main/mdo/continuum.xml?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-model/src/main/mdo/continuum.xml (original)
+++ continuum/trunk/continuum-model/src/main/mdo/continuum.xml Thu Oct 30 17:01:38 2008
@@ -99,6 +99,14 @@
             <multiplicity>*</multiplicity>
           </association>
         </field>
+        <field>
+          <name>projectScmRoots</name>
+          <version>1.1.3+</version>
+          <association>
+            <type>ProjectScmRoot</type>
+            <multiplicity>*</multiplicity>
+          </association>
+        </field>
       </fields>
     </class>
 
@@ -128,7 +136,7 @@
           <version>1.0.9+</version>
           <type>String</type>
         </field>
-        <field jpox.mapped-by="projectGroup" jpox.fetch-groups="projectgroup-projects project-build-details">
+        <field jpox.mapped-by="projectGroup" jpox.fetch-groups="projectgroup-projects project-build-details project-with-scm-result">
           <name>projects</name>
           <version>1.0.9+</version>
           <association jpox.join="false">
@@ -335,7 +343,14 @@
             <type>BuildDefinition</type>
             <multiplicity>*</multiplicity>
           </association>
-        </field>        
+        </field>     
+        <field jpox.fetch-groups="project-all-details project-with-scm-details">
+          <name>scmResult</name>
+          <version>1.1.3+</version>
+          <association>
+            <type>ScmResult</type>
+          </association>
+        </field>
       </fields>
     </class>
 
@@ -418,6 +433,12 @@
             <multiplicity>*</multiplicity>
           </association>
         </field>
+        <field>
+          <name>sendOnScmFailure</name>
+          <version>1.1.3+</version>
+          <type>boolean</type>
+          <defaultValue>false</defaultValue>
+        </field>
       </fields>
       <codeSegments>
         <codeSegment>
@@ -572,6 +593,7 @@
           <version>1.0.9+</version>
           <type>int</type>
         </field>
+        <!-- TODO: remove -->
         <field jpox.fetch-groups="build-result-with-details">
           <name>scmResult</name>
           <version>1.0.9+</version>
@@ -1290,6 +1312,8 @@
     public final static int UPDATING = 8;
     public final static int WARNING = 9;
     public final static int CHECKEDOUT = 10;
+    public final static int UPDATED = 5;
+    public final static int CANCELLED = 11;
 
     // TODO: maybe move these to another class
     public static final int TRIGGER_FORCED = 1;
@@ -1527,5 +1551,49 @@
         </field>
       </fields>
     </class> 
+
+    <class>
+      <name>ProjectScmRoot</name>
+      <version>1.1.3+</version>
+      <packageName>org.apache.continuum.model.project</packageName>
+      <fields>
+        <field>
+          <name>id</name>
+          <version>1.1.3+</version>
+          <identifier>true</identifier>
+          <type>int</type>
+        </field>
+        <field>
+          <name>scmRootAddress</name>
+          <version>1.1.3+</version>
+          <type>String</type>
+          <required>true</required>
+        </field>
+        <field>
+          <name>oldState</name>
+          <version>1.1.3+</version>
+          <type>int</type>
+        </field>
+        <field jpox.column="scmRootState">
+          <name>state</name>
+          <version>1.1.3+</version>
+          <type>int</type>
+        </field>
+        <field stash.maxSize="8192">
+          <name>error</name>
+          <version>1.1.3+</version>
+          <type>String</type>
+        </field>
+        <field>
+          <name>projectGroup</name>
+          <version>1.1.3+</version>
+          <association xml.reference="true" stash.part="true" jpox.dependent="false">
+            <type>ProjectGroup</type>
+          </association>
+          <required>true</required>
+        </field>
+      </fields>
+    </class>
+    
   </classes>
 </model>

Modified: continuum/trunk/continuum-model/src/main/resources/package-mssql.orm
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-model/src/main/resources/package-mssql.orm?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-model/src/main/resources/package-mssql.orm (original)
+++ continuum/trunk/continuum-model/src/main/resources/package-mssql.orm Thu Oct 30 17:01:38 2008
@@ -32,4 +32,11 @@
       </field>
     </class>
   </package>
+  <package name="org.apache.continuum.model.project">
+    <class name="ProjectScmRoot">
+      <field name="error">
+        <column length="4000" jdbc-type="VARCHAR"/>
+      </field>
+    </class>
+  </package>
 </orm>

Modified: continuum/trunk/continuum-notifiers/continuum-notifier-api/src/main/java/org/apache/maven/continuum/notification/AbstractContinuumNotifier.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-notifiers/continuum-notifier-api/src/main/java/org/apache/maven/continuum/notification/AbstractContinuumNotifier.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-notifiers/continuum-notifier-api/src/main/java/org/apache/maven/continuum/notification/AbstractContinuumNotifier.java (original)
+++ continuum/trunk/continuum-notifiers/continuum-notifier-api/src/main/java/org/apache/maven/continuum/notification/AbstractContinuumNotifier.java Thu Oct 30 17:01:38 2008
@@ -22,6 +22,8 @@
 import org.apache.continuum.configuration.ContinuumConfigurationException;
 import org.apache.continuum.dao.BuildResultDao;
 import org.apache.continuum.dao.ProjectDao;
+import org.apache.continuum.dao.ProjectScmRootDao;
+import org.apache.continuum.model.project.ProjectScmRoot;
 import org.apache.maven.continuum.ContinuumException;
 import org.apache.maven.continuum.configuration.ConfigurationException;
 import org.apache.maven.continuum.configuration.ConfigurationLoadingException;
@@ -29,6 +31,7 @@
 import org.apache.maven.continuum.model.project.BuildDefinition;
 import org.apache.maven.continuum.model.project.BuildResult;
 import org.apache.maven.continuum.model.project.Project;
+import org.apache.maven.continuum.model.project.ProjectGroup;
 import org.apache.maven.continuum.model.project.ProjectNotifier;
 import org.apache.maven.continuum.project.ContinuumProjectState;
 import org.apache.maven.continuum.store.ContinuumStoreException;
@@ -62,6 +65,11 @@
     private ProjectDao projectDao;
 
     /**
+     * @plexus.requirement
+     */
+    private ProjectScmRootDao projectScmRootDao;
+
+    /**
      * @plexus.configuration
      */
     private boolean alwaysSend = false;
@@ -135,6 +143,42 @@
         }
     }
 
+    public String getReportUrl( ProjectGroup projectGroup, ProjectScmRoot projectScmRoot, 
+                                ConfigurationService configurationService )
+        throws ContinuumException
+    {
+        try
+        {
+            if ( !configurationService.isLoaded() )
+            {
+                configurationService.reload();
+            }
+    
+            StringBuffer buf = new StringBuffer( configurationService.getUrl() );
+    
+            if ( projectGroup != null && projectScmRoot != null )
+            {
+                if ( !buf.toString().endsWith( "/" ) )                
+                {
+                    buf.append( "/" );
+                }
+
+                buf.append( "scmResult.action?projectScmRootId=" ).append( projectScmRoot.getId() )
+                   .append( "&projectGroupId=" ).append( projectGroup.getId() );
+            }
+    
+            return buf.toString();
+        }
+        catch ( ConfigurationLoadingException e )
+        {
+            throw new ContinuumException( "Can't obtain the base url from configuration.", e );
+        }
+        catch ( ContinuumConfigurationException e )
+        {
+            throw new ContinuumException( "Can't obtain the base url from configuration.", e );
+        }
+    }
+    
     /**
      * Determine if message must be sent
      *
@@ -233,6 +277,31 @@
         return false;
     }
 
+    public boolean shouldNotify( ProjectScmRoot projectScmRoot, ProjectNotifier projectNotifier )
+    {
+        if ( projectNotifier == null )
+        {
+            projectNotifier = new ProjectNotifier();
+        }
+
+        if ( projectScmRoot == null )
+        {
+            return false;
+        }
+
+        if ( alwaysSend )
+        {
+            return true;
+        }
+
+        if ( projectScmRoot.getState() == ContinuumProjectState.ERROR && projectNotifier.isSendOnScmFailure() )
+        {
+            return true;
+        }
+
+        return false;
+    }
+    
     protected BuildResult getPreviousBuild( Project project, BuildDefinition buildDef, BuildResult currentBuild )
         throws NotificationException
     {
@@ -291,4 +360,78 @@
             throw new NotificationException( "Unable to obtain project builds", e );
         }
     }
+    
+    protected String generateMessage( Project project, BuildResult build, ConfigurationService configurationService )
+        throws NotificationException
+    {
+        int state = project.getState();
+    
+        if ( build != null )
+        {
+            state = build.getState();
+        }
+    
+        String message;
+    
+        if ( state == ContinuumProjectState.OK )
+        {
+            message = "BUILD SUCCESSFUL: " + project.getName();
+        }
+        else if ( state == ContinuumProjectState.FAILED )
+        {
+            message = "BUILD FAILURE: " + project.getName();
+        }
+        else if ( state == ContinuumProjectState.ERROR )
+        {
+            message = "BUILD ERROR: " + project.getName();
+        }
+        else
+        {
+            log.warn( "Unknown build state " + state + " for project " + project.getId() );
+    
+            message = "ERROR: Unknown build state " + state + " for " + project.getName() + " project";
+        }
+    
+        try
+        {
+            return message + " " + getReportUrl( project, build, configurationService );
+        }
+        catch ( ContinuumException e )
+        {
+            throw new NotificationException( "Cannot generate message", e );
+        }
+    }
+    
+    protected String generateMessage( ProjectScmRoot projectScmRoot, ConfigurationService configurationService )
+        throws NotificationException
+    {
+        int state = projectScmRoot.getState();
+        String scmRootAddress = projectScmRoot.getScmRootAddress();
+    
+        String message;
+    
+        if ( state == ContinuumProjectState.UPDATED )
+        {
+            message = "PREPARE BUILD SUCCESSFUL: " + scmRootAddress;
+        }
+        else if ( state == ContinuumProjectState.ERROR )
+        {
+            message = "PREPARE BUILD ERROR: " + scmRootAddress;
+        }
+        else
+        {
+            log.warn( "Unknown prepare build state " + state + " for SCM root URL " + scmRootAddress );
+    
+            message = "ERROR: Unknown prepare build state " + state + " for SCM root URL" + scmRootAddress;
+        }
+    
+        try
+        {
+            return message + " " + getReportUrl( projectScmRoot.getProjectGroup(), projectScmRoot, configurationService );
+        }
+        catch ( ContinuumException e )
+        {
+            throw new NotificationException( "Cannot generate message", e );
+        }
+    }
 }

Modified: continuum/trunk/continuum-notifiers/continuum-notifier-api/src/main/java/org/apache/maven/continuum/notification/MessageContext.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-notifiers/continuum-notifier-api/src/main/java/org/apache/maven/continuum/notification/MessageContext.java?rev=709300&r1=709299&r2=709300&view=diff
==============================================================================
--- continuum/trunk/continuum-notifiers/continuum-notifier-api/src/main/java/org/apache/maven/continuum/notification/MessageContext.java (original)
+++ continuum/trunk/continuum-notifiers/continuum-notifier-api/src/main/java/org/apache/maven/continuum/notification/MessageContext.java Thu Oct 30 17:01:38 2008
@@ -19,6 +19,7 @@
  * under the License.
  */
 
+import org.apache.continuum.model.project.ProjectScmRoot;
 import org.apache.maven.continuum.model.project.BuildDefinition;
 import org.apache.maven.continuum.model.project.BuildResult;
 import org.apache.maven.continuum.model.project.Project;
@@ -40,6 +41,8 @@
 
     private BuildResult buildResult;
 
+    private ProjectScmRoot projectScmRoot;
+
     public Project getProject()
     {
         return project;
@@ -79,4 +82,14 @@
     {
         this.buildResult = buildResult;
     }
+
+    public ProjectScmRoot getProjectScmRoot()
+    {
+        return projectScmRoot;
+    }
+
+    public void setProjectScmRoot( ProjectScmRoot projectScmRoot )
+    {
+        this.projectScmRoot = projectScmRoot;
+    }
 }