View Javadoc

1   package org.apache.maven.plugins.site;
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.commons.lang.StringUtils;
23  import org.apache.maven.artifact.manager.WagonConfigurationException;
24  import org.apache.maven.artifact.manager.WagonManager;
25  import org.apache.maven.model.DistributionManagement;
26  import org.apache.maven.model.Site;
27  import org.apache.maven.plugin.AbstractMojo;
28  import org.apache.maven.plugin.MojoExecutionException;
29  import org.apache.maven.plugin.logging.Log;
30  import org.apache.maven.project.MavenProject;
31  import org.apache.maven.settings.Server;
32  import org.apache.maven.settings.Settings;
33  import org.apache.maven.wagon.CommandExecutionException;
34  import org.apache.maven.wagon.CommandExecutor;
35  import org.apache.maven.wagon.ConnectionException;
36  import org.apache.maven.wagon.ResourceDoesNotExistException;
37  import org.apache.maven.wagon.TransferFailedException;
38  import org.apache.maven.wagon.UnsupportedProtocolException;
39  import org.apache.maven.wagon.Wagon;
40  import org.apache.maven.wagon.authentication.AuthenticationException;
41  import org.apache.maven.wagon.authorization.AuthorizationException;
42  import org.apache.maven.wagon.observers.Debug;
43  import org.apache.maven.wagon.proxy.ProxyInfo;
44  import org.apache.maven.wagon.repository.Repository;
45  import org.codehaus.plexus.PlexusConstants;
46  import org.codehaus.plexus.PlexusContainer;
47  import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
48  import org.codehaus.plexus.component.configurator.ComponentConfigurator;
49  import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
50  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
51  import org.codehaus.plexus.configuration.PlexusConfiguration;
52  import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
53  import org.codehaus.plexus.context.Context;
54  import org.codehaus.plexus.context.ContextException;
55  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
56  import org.codehaus.plexus.util.xml.Xpp3Dom;
57  
58  import java.io.File;
59  
60  /**
61   * Deploys the generated site using <code>scp</code> or <code>file</code>
62   * protocol to the site URL specified in the
63   * <code>&lt;distributionManagement&gt;</code> section of the POM.
64   * <p>
65   * For <code>scp</code> protocol, the website files are packaged into zip archive,
66   * then the archive is transfered to the remote host, next it is un-archived.
67   * This method of deployment should normally be much faster
68   * than making a file by file copy.  For <code>file</code> protocol, the files are copied
69   * directly to the destination directory.
70   * </p>
71   *
72   * @author <a href="mailto:michal@org.codehaus.org">Michal Maczka</a>
73   * @version $Id: SiteDeployMojo.html 816552 2012-05-08 11:54:37Z hboutemy $
74   * @goal deploy
75   */
76  public class SiteDeployMojo
77      extends AbstractMojo implements Contextualizable
78  {
79      /**
80       * Directory containing the generated project sites and report distributions.
81       *
82       * @parameter alias="outputDirectory" expression="${project.reporting.outputDirectory}"
83       * @required
84       */
85      private File inputDirectory;
86  
87      /**
88       * Whether to run the "chmod" command on the remote site after the deploy.
89       * Defaults to "true".
90       *
91       * @parameter expression="${maven.site.chmod}" default-value="true"
92       * @since 2.1
93       */
94      private boolean chmod;
95  
96      /**
97       * The mode used by the "chmod" command. Only used if chmod = true.
98       * Defaults to "g+w,a+rX".
99       *
100      * @parameter expression="${maven.site.chmod.mode}" default-value="g+w,a+rX"
101      * @since 2.1
102      */
103     private String chmodMode;
104 
105     /**
106      * The options used by the "chmod" command. Only used if chmod = true.
107      * Defaults to "-Rf".
108      *
109      * @parameter expression="${maven.site.chmod.options}" default-value="-Rf"
110      * @since 2.1
111      */
112     private String chmodOptions;
113 
114     /**
115      * @parameter expression="${project}"
116      * @required
117      * @readonly
118      */
119     private MavenProject project;
120 
121     /**
122      * @component
123      */
124     private WagonManager wagonManager;
125 
126     /**
127      * The current user system settings for use in Maven.
128      *
129      * @parameter expression="${settings}"
130      * @required
131      * @readonly
132      */
133     private Settings settings;
134 
135     private PlexusContainer container;
136 
137     /** {@inheritDoc} */
138     public void execute()
139         throws MojoExecutionException
140     {
141         if ( !inputDirectory.exists() )
142         {
143             throw new MojoExecutionException( "The site does not exist, please run site:site first" );
144         }
145 
146         DistributionManagement distributionManagement = project.getDistributionManagement();
147 
148         if ( distributionManagement == null )
149         {
150             throw new MojoExecutionException( "Missing distribution management information in the project" );
151         }
152 
153         Site site = distributionManagement.getSite();
154 
155         if ( site == null )
156         {
157             throw new MojoExecutionException(
158                 "Missing site information in the distribution management element in the project.." );
159         }
160 
161         String url = site.getUrl();
162 
163         String id = site.getId();
164 
165         if ( url == null )
166         {
167             throw new MojoExecutionException( "The URL to the site is missing in the project descriptor." );
168         }
169         getLog().debug( "The site will be deployed to '" + url + "'");
170 
171         Repository repository = new Repository( id, url );
172 
173         // TODO: work on moving this into the deployer like the other deploy methods
174 
175         Wagon wagon;
176 
177         try
178         {
179             wagon = wagonManager.getWagon( repository );
180             configureWagon( wagon, repository.getId(), settings, container, getLog() );
181         }
182         catch ( UnsupportedProtocolException e )
183         {
184             throw new MojoExecutionException( "Unsupported protocol: '" + repository.getProtocol() + "'", e );
185         }
186         catch ( WagonConfigurationException e )
187         {
188             throw new MojoExecutionException( "Unable to configure Wagon: '" + repository.getProtocol() + "'", e );
189         }
190 
191         if ( !wagon.supportsDirectoryCopy() )
192         {
193             throw new MojoExecutionException(
194                 "Wagon protocol '" + repository.getProtocol() + "' doesn't support directory copying" );
195         }
196 
197         try
198         {
199             Debug debug = new Debug();
200 
201             wagon.addSessionListener( debug );
202 
203             wagon.addTransferListener( debug );
204 
205             ProxyInfo proxyInfo = getProxyInfo( repository, wagonManager );
206             if ( proxyInfo != null )
207             {
208                 wagon.connect( repository, wagonManager.getAuthenticationInfo( id ), proxyInfo );
209             }
210             else
211             {
212                 wagon.connect( repository, wagonManager.getAuthenticationInfo( id ) );
213             }
214 
215             wagon.putDirectory( inputDirectory, "." );
216 
217             if ( chmod && wagon instanceof CommandExecutor )
218             {
219                 CommandExecutor exec = (CommandExecutor) wagon;
220                 exec.executeCommand( "chmod " + chmodOptions + " " + chmodMode + " " + repository.getBasedir() );
221             }
222         }
223         catch ( ResourceDoesNotExistException e )
224         {
225             throw new MojoExecutionException( "Error uploading site", e );
226         }
227         catch ( TransferFailedException e )
228         {
229             throw new MojoExecutionException( "Error uploading site", e );
230         }
231         catch ( AuthorizationException e )
232         {
233             throw new MojoExecutionException( "Error uploading site", e );
234         }
235         catch ( ConnectionException e )
236         {
237             throw new MojoExecutionException( "Error uploading site", e );
238         }
239         catch ( AuthenticationException e )
240         {
241             throw new MojoExecutionException( "Error uploading site", e );
242         }
243         catch ( CommandExecutionException e )
244         {
245             throw new MojoExecutionException( "Error uploading site", e );
246         }
247         finally
248         {
249             try
250             {
251                 wagon.disconnect();
252             }
253             catch ( ConnectionException e )
254             {
255                 getLog().error( "Error disconnecting wagon - ignored", e );
256             }
257         }
258     }
259 
260     /**
261      * <p>
262      * Get the <code>ProxyInfo</code> of the proxy associated with the <code>host</code>
263      * and the <code>protocol</code> of the given <code>repository</code>.
264      * </p>
265      * <p>
266      * Extract from <a href="http://java.sun.com/j2se/1.5.0/docs/guide/net/properties.html">
267      * J2SE Doc : Networking Properties - nonProxyHosts</a> : "The value can be a list of hosts,
268      * each separated by a |, and in addition a wildcard character (*) can be used for matching"
269      * </p>
270      * <p>
271      * Defensively support for comma (",") and semi colon (";") in addition to pipe ("|") as separator.
272      * </p>
273      *
274      * @param repository the Repository to extract the ProxyInfo from.
275      * @param wagonManager the WagonManager used to connect to the Repository.
276      * @return a ProxyInfo object instantiated or <code>null</code> if no matching proxy is found
277      */
278     public static ProxyInfo getProxyInfo( Repository repository, WagonManager wagonManager )
279     {
280         ProxyInfo proxyInfo = wagonManager.getProxy( repository.getProtocol() );
281 
282         if ( proxyInfo == null )
283         {
284             return null;
285         }
286 
287         String host = repository.getHost();
288         String nonProxyHostsAsString = proxyInfo.getNonProxyHosts();
289         String[] nonProxyHosts = StringUtils.split( nonProxyHostsAsString, ",;|" );
290         for ( int i = 0; i < nonProxyHosts.length; i++ )
291         {
292             String nonProxyHost = nonProxyHosts[i];
293             if ( StringUtils.contains( nonProxyHost, "*" ) )
294             {
295                 // Handle wildcard at the end, beginning or middle of the nonProxyHost
296                 String nonProxyHostPrefix = StringUtils.substringBefore( nonProxyHost, "*" );
297                 String nonProxyHostSuffix = StringUtils.substringAfter( nonProxyHost, "*" );
298                 // prefix*
299                 if ( StringUtils.isNotEmpty( nonProxyHostPrefix ) && host.startsWith( nonProxyHostPrefix )
300                     && StringUtils.isEmpty( nonProxyHostSuffix ) )
301                 {
302                     return null;
303                 }
304                 // *suffix
305                 if ( StringUtils.isEmpty( nonProxyHostPrefix )
306                     && StringUtils.isNotEmpty( nonProxyHostSuffix ) && host.endsWith( nonProxyHostSuffix ) )
307                 {
308                     return null;
309                 }
310                 // prefix*suffix
311                 if ( StringUtils.isNotEmpty( nonProxyHostPrefix ) && host.startsWith( nonProxyHostPrefix )
312                     && StringUtils.isNotEmpty( nonProxyHostSuffix ) && host.endsWith( nonProxyHostSuffix ) )
313                 {
314                     return null;
315                 }
316             }
317             else if ( host.equals( nonProxyHost ) )
318             {
319                 return null;
320             }
321         }
322         return proxyInfo;
323     }
324 
325     /**
326      * Configure the Wagon with the information from serverConfigurationMap ( which comes from settings.xml )
327      *
328      * @todo Remove when {@link WagonManager#getWagon(Repository) is available}. It's available in Maven 2.0.5.
329      * @param wagon
330      * @param repositoryId
331      * @param settings
332      * @param container
333      * @param log
334      * @throws WagonConfigurationException
335      */
336     static void configureWagon( Wagon wagon, String repositoryId, Settings settings, PlexusContainer container,
337                                 Log log )
338         throws WagonConfigurationException
339     {
340         // MSITE-25: Make sure that the server settings are inserted
341         for ( int i = 0; i < settings.getServers().size(); i++ )
342         {
343             Server server = (Server) settings.getServers().get( i );
344             String id = server.getId();
345             if ( id != null && id.equals( repositoryId ) )
346             {
347                 if ( server.getConfiguration() != null )
348                 {
349                     final PlexusConfiguration plexusConf =
350                         new XmlPlexusConfiguration( (Xpp3Dom) server.getConfiguration() );
351 
352                     ComponentConfigurator componentConfigurator = null;
353                     try
354                     {
355                         componentConfigurator = (ComponentConfigurator) container.lookup( ComponentConfigurator.ROLE );
356                         componentConfigurator.configureComponent( wagon, plexusConf, container.getContainerRealm() );
357                     }
358                     catch ( final ComponentLookupException e )
359                     {
360                         throw new WagonConfigurationException( repositoryId, "Unable to lookup wagon configurator."
361                             + " Wagon configuration cannot be applied.", e );
362                     }
363                     catch ( ComponentConfigurationException e )
364                     {
365                         throw new WagonConfigurationException( repositoryId, "Unable to apply wagon configuration.",
366                                                                e );
367                     }
368                     finally
369                     {
370                         if ( componentConfigurator != null )
371                         {
372                             try
373                             {
374                                 container.release( componentConfigurator );
375                             }
376                             catch ( ComponentLifecycleException e )
377                             {
378                                 log.error( "Problem releasing configurator - ignoring: " + e.getMessage() );
379                             }
380                         }
381                     }
382 
383                 }
384 
385             }
386         }
387     }
388 
389     public void contextualize( Context context )
390         throws ContextException
391     {
392         container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
393     }
394 
395 }