View Javadoc

1   package org.apache.maven.continuum.notification;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.continuum.configuration.ContinuumConfigurationException;
23  import org.apache.continuum.dao.BuildResultDao;
24  import org.apache.continuum.dao.ProjectDao;
25  import org.apache.continuum.dao.ProjectScmRootDao;
26  import org.apache.continuum.model.project.ProjectScmRoot;
27  import org.apache.maven.continuum.ContinuumException;
28  import org.apache.maven.continuum.configuration.ConfigurationException;
29  import org.apache.maven.continuum.configuration.ConfigurationLoadingException;
30  import org.apache.maven.continuum.configuration.ConfigurationService;
31  import org.apache.maven.continuum.model.project.BuildDefinition;
32  import org.apache.maven.continuum.model.project.BuildResult;
33  import org.apache.maven.continuum.model.project.Project;
34  import org.apache.maven.continuum.model.project.ProjectGroup;
35  import org.apache.maven.continuum.model.project.ProjectNotifier;
36  import org.apache.maven.continuum.project.ContinuumProjectState;
37  import org.apache.maven.continuum.store.ContinuumStoreException;
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  
41  import java.util.List;
42  import javax.annotation.Resource;
43  
44  public abstract class AbstractContinuumNotifier
45      implements Notifier
46  {
47      public static final String ADDRESS_FIELD = "address";
48  
49      public static final String COMMITTER_FIELD = "committers";
50  
51      public static final String DEVELOPER_FIELD = "developers";
52  
53      private static final Logger log = LoggerFactory.getLogger( AbstractContinuumNotifier.class );
54  
55      @Resource
56      private ConfigurationService configurationService;
57  
58      @Resource
59      private BuildResultDao buildResultDao;
60  
61      @Resource
62      private ProjectDao projectDao;
63  
64      @Resource
65      private ProjectScmRootDao projectScmRootDao;
66  
67      private boolean alwaysSend = false;
68  
69      protected String getBuildOutput( Project project, BuildResult buildResult )
70      {
71          if ( buildResult == null )
72          {
73              return "";
74          }
75          try
76          {
77              if ( buildResult.getEndTime() != 0 )
78              {
79                  return configurationService.getBuildOutput( buildResult.getId(), project.getId() );
80              }
81              else
82              {
83                  return "";
84              }
85          }
86          catch ( ConfigurationException e )
87          {
88              String msg = "Error while population the notification context.";
89              log.error( msg, e );
90              return msg;
91          }
92      }
93  
94      /**
95       * Returns url of the last build
96       *
97       * @param project              The project
98       * @param build                The build
99       * @param configurationService The configuration Service
100      * @return The report URL
101      * @throws ContinuumException whne the configuration can't be loaded
102      */
103     public String getReportUrl( Project project, BuildResult build, ConfigurationService configurationService )
104         throws ContinuumException
105     {
106         try
107         {
108             if ( !configurationService.isLoaded() )
109             {
110                 configurationService.reload();
111             }
112 
113             StringBuffer buf = new StringBuffer( configurationService.getUrl() );
114 
115             if ( project != null && build != null )
116             {
117                 if ( !buf.toString().endsWith( "/" ) )
118                 {
119                     buf.append( "/" );
120                 }
121 
122                 buf.append( "buildResult.action?buildId=" ).append( build.getId() ).append( "&projectId=" ).append(
123                     project.getId() );
124             }
125 
126             return buf.toString();
127         }
128         catch ( ConfigurationLoadingException e )
129         {
130             throw new ContinuumException( "Can't obtain the base url from configuration.", e );
131         }
132         catch ( ContinuumConfigurationException e )
133         {
134             throw new ContinuumException( "Can't obtain the base url from configuration.", e );
135         }
136     }
137 
138     public String getReportUrl( ProjectGroup projectGroup, ProjectScmRoot projectScmRoot,
139                                 ConfigurationService configurationService )
140         throws ContinuumException
141     {
142         try
143         {
144             if ( !configurationService.isLoaded() )
145             {
146                 configurationService.reload();
147             }
148 
149             StringBuffer buf = new StringBuffer( configurationService.getUrl() );
150 
151             if ( projectGroup != null && projectScmRoot != null )
152             {
153                 if ( !buf.toString().endsWith( "/" ) )
154                 {
155                     buf.append( "/" );
156                 }
157 
158                 buf.append( "scmResult.action?projectScmRootId=" ).append( projectScmRoot.getId() ).append(
159                     "&projectGroupId=" ).append( projectGroup.getId() );
160             }
161 
162             return buf.toString();
163         }
164         catch ( ConfigurationLoadingException e )
165         {
166             throw new ContinuumException( "Can't obtain the base url from configuration.", e );
167         }
168         catch ( ContinuumConfigurationException e )
169         {
170             throw new ContinuumException( "Can't obtain the base url from configuration.", e );
171         }
172     }
173 
174     /**
175      * Determine if message must be sent
176      *
177      * @param build           The current build result
178      * @param previousBuild   The previous build result
179      * @param projectNotifier The project notifier
180      * @return True if a message must be sent
181      */
182     public boolean shouldNotify( BuildResult build, BuildResult previousBuild, ProjectNotifier projectNotifier )
183     {
184         if ( projectNotifier == null )
185         {
186             projectNotifier = new ProjectNotifier();
187         }
188 
189         if ( build == null )
190         {
191             return false;
192         }
193 
194         if ( alwaysSend )
195         {
196             return true;
197         }
198 
199         if ( build.getState() == ContinuumProjectState.FAILED && projectNotifier.isSendOnFailure() )
200         {
201             return true;
202         }
203 
204         if ( build.getState() == ContinuumProjectState.ERROR && projectNotifier.isSendOnError() )
205         {
206             return true;
207         }
208 
209         // Send if this is the first build
210         if ( previousBuild == null )
211         {
212             if ( build.getState() == ContinuumProjectState.ERROR )
213             {
214                 return projectNotifier.isSendOnError();
215             }
216 
217             if ( build.getState() == ContinuumProjectState.FAILED )
218             {
219                 return projectNotifier.isSendOnFailure();
220             }
221 
222             if ( build.getState() == ContinuumProjectState.OK )
223             {
224                 return projectNotifier.isSendOnSuccess();
225             }
226 
227             return build.getState() != ContinuumProjectState.WARNING || projectNotifier.isSendOnWarning();
228 
229         }
230 
231         // Send if the state has changed
232         if ( log.isDebugEnabled() )
233         {
234             log.debug(
235                 "Current build state: " + build.getState() + ", previous build state: " + previousBuild.getState() );
236         }
237 
238         if ( build.getState() != previousBuild.getState() )
239         {
240             if ( build.getState() == ContinuumProjectState.ERROR )
241             {
242                 return projectNotifier.isSendOnError();
243             }
244 
245             if ( build.getState() == ContinuumProjectState.FAILED )
246             {
247                 return projectNotifier.isSendOnFailure();
248             }
249 
250             if ( build.getState() == ContinuumProjectState.OK )
251             {
252                 return projectNotifier.isSendOnSuccess();
253             }
254 
255             return build.getState() != ContinuumProjectState.WARNING || projectNotifier.isSendOnWarning();
256 
257         }
258 
259         log.info( "Same state, not sending message." );
260 
261         return false;
262     }
263 
264     public boolean shouldNotify( ProjectScmRoot projectScmRoot, ProjectNotifier projectNotifier )
265     {
266         if ( projectNotifier == null )
267         {
268             projectNotifier = new ProjectNotifier();
269         }
270 
271         return projectScmRoot != null && ( alwaysSend ||
272             projectScmRoot.getState() == ContinuumProjectState.ERROR && projectNotifier.isSendOnScmFailure() &&
273                 projectScmRoot.getOldState() != projectScmRoot.getState() );
274 
275     }
276 
277     protected BuildResult getPreviousBuild( Project project, BuildDefinition buildDef, BuildResult currentBuild )
278         throws NotificationException
279     {
280         List<BuildResult> builds;
281         try
282         {
283             if ( buildDef != null )
284             {
285                 builds = buildResultDao.getBuildResultsByBuildDefinition( project.getId(), buildDef.getId(), 0, 2 );
286 
287                 if ( builds.size() < 2 )
288                 {
289                     return null;
290                 }
291                 //builds are sorted in descending way
292                 BuildResult build = builds.get( 0 );
293                 if ( currentBuild != null && build.getId() != currentBuild.getId() )
294                 {
295                     throw new NotificationException(
296                         "INTERNAL ERROR: The current build wasn't the first in the build list. " + "Current build: '" +
297                             currentBuild.getId() + "', " + "first build: '" + build.getId() + "'." );
298                 }
299                 else
300                 {
301                     return builds.get( 1 );
302                 }
303             }
304             else
305             {
306                 //Normally, it isn't possible, buildDef should be != null
307                 if ( project.getId() > 0 )
308                 {
309                     project = projectDao.getProjectWithBuilds( project.getId() );
310                 }
311                 builds = project.getBuildResults();
312 
313                 if ( builds.size() < 2 )
314                 {
315                     return null;
316                 }
317 
318                 BuildResult build = builds.get( builds.size() - 1 );
319 
320                 if ( currentBuild != null && build.getId() != currentBuild.getId() )
321                 {
322                     throw new NotificationException(
323                         "INTERNAL ERROR: The current build wasn't the first in the build list. " + "Current build: '" +
324                             currentBuild.getId() + "', " + "first build: '" + build.getId() + "'." );
325                 }
326 
327                 return builds.get( builds.size() - 2 );
328             }
329         }
330         catch ( ContinuumStoreException e )
331         {
332             throw new NotificationException( "Unable to obtain project builds", e );
333         }
334     }
335 
336     protected String generateMessage( Project project, BuildResult build, ConfigurationService configurationService )
337         throws NotificationException
338     {
339         int state = project.getState();
340 
341         if ( build != null )
342         {
343             state = build.getState();
344         }
345 
346         String message;
347 
348         if ( state == ContinuumProjectState.OK )
349         {
350             message = "BUILD SUCCESSFUL: " + project.getName();
351         }
352         else if ( state == ContinuumProjectState.FAILED )
353         {
354             message = "BUILD FAILURE: " + project.getName();
355         }
356         else if ( state == ContinuumProjectState.ERROR )
357         {
358             message = "BUILD ERROR: " + project.getName();
359         }
360         else
361         {
362             log.warn( "Unknown build state " + state + " for project " + project.getId() );
363 
364             message = "ERROR: Unknown build state " + state + " for " + project.getName() + " project";
365         }
366 
367         try
368         {
369             return message + " " + getReportUrl( project, build, configurationService );
370         }
371         catch ( ContinuumException e )
372         {
373             throw new NotificationException( "Cannot generate message", e );
374         }
375     }
376 
377     protected String generateMessage( ProjectScmRoot projectScmRoot, ConfigurationService configurationService )
378         throws NotificationException
379     {
380         int state = projectScmRoot.getState();
381         String scmRootAddress = projectScmRoot.getScmRootAddress();
382 
383         String message;
384 
385         if ( state == ContinuumProjectState.UPDATED )
386         {
387             message = "PREPARE BUILD SUCCESSFUL: " + scmRootAddress;
388         }
389         else if ( state == ContinuumProjectState.ERROR )
390         {
391             message = "PREPARE BUILD ERROR: " + scmRootAddress;
392         }
393         else
394         {
395             log.warn( "Unknown prepare build state " + state + " for SCM root URL " + scmRootAddress );
396 
397             message = "ERROR: Unknown prepare build state " + state + " for SCM root URL" + scmRootAddress;
398         }
399 
400         try
401         {
402             return message + " " +
403                 getReportUrl( projectScmRoot.getProjectGroup(), projectScmRoot, configurationService );
404         }
405         catch ( ContinuumException e )
406         {
407             throw new NotificationException( "Cannot generate message", e );
408         }
409     }
410 }