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