View Javadoc

1   package org.apache.maven.continuum.notification.wagon;
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.maven.artifact.manager.WagonManager;
23  import org.apache.maven.artifact.repository.ArtifactRepository;
24  import org.apache.maven.artifact.repository.DefaultArtifactRepository;
25  import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
26  import org.apache.maven.continuum.ContinuumException;
27  import org.apache.maven.continuum.configuration.ConfigurationException;
28  import org.apache.maven.continuum.configuration.ConfigurationService;
29  import org.apache.maven.continuum.model.project.BuildDefinition;
30  import org.apache.maven.continuum.model.project.BuildResult;
31  import org.apache.maven.continuum.model.project.Project;
32  import org.apache.maven.continuum.model.project.ProjectNotifier;
33  import org.apache.maven.continuum.notification.AbstractContinuumNotifier;
34  import org.apache.maven.continuum.notification.ContinuumNotificationDispatcher;
35  import org.apache.maven.continuum.notification.MessageContext;
36  import org.apache.maven.continuum.notification.NotificationException;
37  import org.apache.maven.model.DistributionManagement;
38  import org.apache.maven.model.Site;
39  import org.apache.maven.profiles.DefaultProfileManager;
40  import org.apache.maven.profiles.ProfileManager;
41  import org.apache.maven.project.MavenProject;
42  import org.apache.maven.project.MavenProjectBuilder;
43  import org.apache.maven.project.ProjectBuildingException;
44  import org.apache.maven.settings.MavenSettingsBuilder;
45  import org.apache.maven.settings.Proxy;
46  import org.apache.maven.settings.Server;
47  import org.apache.maven.settings.Settings;
48  import org.apache.maven.wagon.CommandExecutionException;
49  import org.apache.maven.wagon.CommandExecutor;
50  import org.apache.maven.wagon.ConnectionException;
51  import org.apache.maven.wagon.ResourceDoesNotExistException;
52  import org.apache.maven.wagon.TransferFailedException;
53  import org.apache.maven.wagon.UnsupportedProtocolException;
54  import org.apache.maven.wagon.Wagon;
55  import org.apache.maven.wagon.authentication.AuthenticationException;
56  import org.apache.maven.wagon.authentication.AuthenticationInfo;
57  import org.apache.maven.wagon.authorization.AuthorizationException;
58  import org.apache.maven.wagon.observers.Debug;
59  import org.apache.maven.wagon.proxy.ProxyInfo;
60  import org.apache.maven.wagon.repository.Repository;
61  import org.codehaus.plexus.PlexusConstants;
62  import org.codehaus.plexus.PlexusContainer;
63  import org.codehaus.plexus.context.Context;
64  import org.codehaus.plexus.context.ContextException;
65  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
66  import org.codehaus.plexus.util.StringUtils;
67  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
68  import org.slf4j.Logger;
69  import org.slf4j.LoggerFactory;
70  import org.springframework.stereotype.Service;
71  
72  import java.io.File;
73  import java.io.IOException;
74  import java.util.List;
75  import java.util.Map;
76  import javax.annotation.Resource;
77  
78  /**
79   * @author <a href="mailto:hisidro@exist.com">Henry Isidro</a>
80   * @author <a href="mailto:nramirez@exist.com">Napoleon Esmundo C. Ramirez</a>
81   */
82  @Service( "notifier#wagon" )
83  public class WagonContinuumNotifier
84      extends AbstractContinuumNotifier
85      implements Contextualizable
86  {
87      public static final String BUILD_OUTPUT_FILE_NAME = "buildresult.txt";
88  
89      private static final Logger log = LoggerFactory.getLogger( WagonContinuumNotifier.class );
90  
91      @Resource
92      private ConfigurationService configurationService;
93  
94      @Resource
95      private WagonManager wagonManager;
96  
97      @Resource
98      private MavenProjectBuilder projectBuilder;
99  
100     @Resource
101     private MavenSettingsBuilder settingsBuilder;
102 
103     /**
104      * @plexus.configuration
105      */
106     private String localRepository;
107 
108     private Settings settings;
109 
110     private ProfileManager profileManager;
111 
112     private PlexusContainer container;
113 
114     public String getType()
115     {
116         return "wagon";
117     }
118 
119     public void sendMessage( String messageId, MessageContext context )
120         throws NotificationException
121     {
122         Project project = context.getProject();
123 
124         List<ProjectNotifier> notifiers = context.getNotifiers();
125 
126         BuildResult build = context.getBuildResult();
127 
128         BuildDefinition buildDefinition = context.getBuildDefinition();
129 
130         // ----------------------------------------------------------------------
131         // If there wasn't any building done, don't notify
132         // ----------------------------------------------------------------------
133         if ( build == null )
134         {
135             return;
136         }
137 
138         // ----------------------------------------------------------------------
139         // Deloy build result to given url 
140         // ----------------------------------------------------------------------
141         try
142         {
143             /*
144              * acquire the MavenProject associated to the Project in context
145              */
146             MavenProject mavenProject = getMavenProject( project, buildDefinition );
147 
148             if ( messageId.equals( ContinuumNotificationDispatcher.MESSAGE_ID_BUILD_COMPLETE ) )
149             {
150                 for ( ProjectNotifier notifier : notifiers )
151                 {
152                     buildComplete( notifier, build, mavenProject );
153                 }
154             }
155         }
156         catch ( ContinuumException e )
157         {
158             throw new NotificationException( "Error while notifiying.", e );
159         }
160     }
161 
162     private void buildComplete( ProjectNotifier notifier, BuildResult build, MavenProject mavenProject )
163         throws ContinuumException
164     {
165         String id;
166         String url;
167 
168         Map<String, String> configuration = notifier.getConfiguration();
169 
170         if ( configuration.containsKey( "url" ) )
171         {
172             url = configuration.get( "url" );
173             id = configuration.get( "id" );
174         }
175         else
176         {
177             DistributionManagement distributionManagement = mavenProject.getDistributionManagement();
178 
179             if ( distributionManagement == null )
180             {
181                 throw new ContinuumException( "Missing distribution management information in the project." );
182             }
183 
184             Site site = distributionManagement.getSite();
185             if ( site == null )
186             {
187                 throw new ContinuumException(
188                     "Missing site information in the distribution management element in the project." );
189             }
190 
191             url = site.getUrl();
192             id = site.getId();
193         }
194 
195         if ( url == null )
196         {
197             throw new ContinuumException( "The URL to the site is not defined." );
198         }
199 
200         Repository repository = new Repository( id, url );
201 
202         Wagon wagon;
203         try
204         {
205             wagon = wagonManager.getWagon( repository.getProtocol() );
206         }
207         catch ( UnsupportedProtocolException e )
208         {
209             throw new ContinuumException( "Unsupported protocol: '" + repository.getProtocol() + "'", e );
210         }
211 
212         if ( !wagon.supportsDirectoryCopy() )
213         {
214             throw new ContinuumException(
215                 "Wagon protocol '" + repository.getProtocol() + "' doesn't support directory copying" );
216         }
217 
218         try
219         {
220             if ( log.isDebugEnabled() )
221             {
222                 Debug debug = new Debug();
223 
224                 wagon.addSessionListener( debug );
225                 wagon.addTransferListener( debug );
226             }
227 
228             ProxyInfo proxyInfo = getProxyInfo( repository );
229 
230             if ( proxyInfo != null )
231             {
232                 wagon.connect( repository, getAuthenticationInfo( id ), proxyInfo );
233             }
234             else
235             {
236                 wagon.connect( repository, getAuthenticationInfo( id ) );
237             }
238 
239             File buildOutputFile = configurationService.getBuildOutputFile( build.getId(), build.getProject().getId() );
240 
241             wagon.put( buildOutputFile, BUILD_OUTPUT_FILE_NAME );
242 
243             // TODO: current wagon uses zip which will use the umask on remote host instead of honouring our settings
244             //  Force group writeable
245             if ( wagon instanceof CommandExecutor )
246             {
247                 CommandExecutor exec = (CommandExecutor) wagon;
248                 exec.executeCommand( "chmod -Rf g+w " + repository.getBasedir() );
249             }
250         }
251         catch ( ConfigurationException e )
252         {
253             throw new ContinuumException( "Error uploading build results to deployed site.", e );
254         }
255         catch ( ResourceDoesNotExistException e )
256         {
257             throw new ContinuumException( "Error uploading site", e );
258         }
259         catch ( TransferFailedException e )
260         {
261             throw new ContinuumException( "Error uploading site", e );
262         }
263         catch ( AuthorizationException e )
264         {
265             throw new ContinuumException( "Error uploading site", e );
266         }
267         catch ( ConnectionException e )
268         {
269             throw new ContinuumException( "Error uploading site", e );
270         }
271         catch ( AuthenticationException e )
272         {
273             throw new ContinuumException( "Error uploading site", e );
274         }
275         catch ( CommandExecutionException e )
276         {
277             throw new ContinuumException( "Error uploading site", e );
278         }
279         finally
280         {
281             try
282             {
283                 wagon.disconnect();
284             }
285             catch ( ConnectionException e )
286             {
287                 log.error( "Error disconnecting wagon - ignored", e );
288             }
289         }
290     }
291 
292     private MavenProject getMavenProject( Project project, BuildDefinition buildDefinition )
293         throws ContinuumException
294     {
295         File projectWorkingDir = new File( configurationService.getWorkingDirectory(), Integer.toString(
296             project.getId() ) );
297         File pomFile = new File( projectWorkingDir, buildDefinition.getBuildFile() );
298 
299         MavenProject mavenProject;
300 
301         try
302         {
303             mavenProject = projectBuilder.build( pomFile, getLocalRepository(), getProfileManager() );
304         }
305         catch ( ProjectBuildingException e )
306         {
307             throw new ContinuumException( "Unable to acquire the MavenProject in " + pomFile.getAbsolutePath(), e );
308         }
309 
310         return mavenProject;
311     }
312 
313     private Settings getSettings()
314     {
315         if ( settings == null )
316         {
317             try
318             {
319                 settings = settingsBuilder.buildSettings();
320             }
321             catch ( IOException e )
322             {
323                 log.error( "Failed to get Settings", e );
324             }
325             catch ( XmlPullParserException e )
326             {
327                 log.error( "Failed to get Settings", e );
328             }
329         }
330 
331         return settings;
332     }
333 
334     private ArtifactRepository getLocalRepository()
335     {
336         String repo = localRepository;
337 
338         if ( getSettings() != null && !StringUtils.isEmpty( getSettings().getLocalRepository() ) )
339         {
340             repo = getSettings().getLocalRepository();
341         }
342 
343         return new DefaultArtifactRepository( "local-repository", "file://" + repo, new DefaultRepositoryLayout() );
344     }
345 
346     private ProfileManager getProfileManager()
347     {
348         if ( profileManager == null )
349         {
350             profileManager = new DefaultProfileManager( container, getSettings() );
351         }
352 
353         return profileManager;
354     }
355 
356     public void contextualize( Context context )
357         throws ContextException
358     {
359         container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
360     }
361 
362     private ProxyInfo getProxyInfo( Repository repository )
363     {
364         Settings settings = getSettings();
365         if ( settings.getProxies() != null && !settings.getProxies().isEmpty() )
366         {
367             for ( Proxy p : (List<Proxy>) settings.getProxies() )
368             {
369                 wagonManager.addProxy( p.getProtocol(), p.getHost(), p.getPort(), p.getUsername(), p.getPassword(),
370                                        p.getNonProxyHosts() );
371             }
372         }
373         return wagonManager.getProxy( repository.getProtocol() );
374     }
375 
376     private AuthenticationInfo getAuthenticationInfo( String repositoryId )
377     {
378         Settings settings = getSettings();
379         Server server = settings.getServer( repositoryId );
380 
381         if ( server == null )
382         {
383             return null;
384         }
385 
386         wagonManager.addAuthenticationInfo( repositoryId, server.getUsername(), server.getPassword(),
387                                             server.getPrivateKey(), server.getPassphrase() );
388         return wagonManager.getAuthenticationInfo( repositoryId );
389     }
390 }