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