1 package org.apache.maven.plugins.site;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.artifact.manager.WagonManager;
23 import org.apache.maven.doxia.site.decoration.inheritance.URIPathDescriptor;
24 import org.apache.maven.execution.MavenExecutionRequest;
25 import org.apache.maven.execution.MavenSession;
26 import org.apache.maven.model.DistributionManagement;
27 import org.apache.maven.model.Site;
28 import org.apache.maven.plugin.MojoExecutionException;
29 import org.apache.maven.plugin.logging.Log;
30 import org.apache.maven.plugins.annotations.Component;
31 import org.apache.maven.plugins.annotations.Parameter;
32 import org.apache.maven.project.MavenProject;
33 import org.apache.maven.settings.Proxy;
34 import org.apache.maven.settings.Server;
35 import org.apache.maven.settings.Settings;
36 import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
37 import org.apache.maven.settings.crypto.SettingsDecrypter;
38 import org.apache.maven.settings.crypto.SettingsDecryptionResult;
39 import org.apache.maven.wagon.CommandExecutionException;
40 import org.apache.maven.wagon.CommandExecutor;
41 import org.apache.maven.wagon.ConnectionException;
42 import org.apache.maven.wagon.ResourceDoesNotExistException;
43 import org.apache.maven.wagon.TransferFailedException;
44 import org.apache.maven.wagon.UnsupportedProtocolException;
45 import org.apache.maven.wagon.Wagon;
46 import org.apache.maven.wagon.authentication.AuthenticationException;
47 import org.apache.maven.wagon.authentication.AuthenticationInfo;
48 import org.apache.maven.wagon.authorization.AuthorizationException;
49 import org.apache.maven.wagon.observers.Debug;
50 import org.apache.maven.wagon.proxy.ProxyInfo;
51 import org.apache.maven.wagon.repository.Repository;
52 import org.codehaus.classworlds.ClassRealm;
53 import org.codehaus.plexus.PlexusConstants;
54 import org.codehaus.plexus.PlexusContainer;
55 import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
56 import org.codehaus.plexus.component.configurator.ComponentConfigurator;
57 import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
58 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
59 import org.codehaus.plexus.configuration.PlexusConfiguration;
60 import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
61 import org.codehaus.plexus.context.Context;
62 import org.codehaus.plexus.context.ContextException;
63 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
64 import org.codehaus.plexus.util.StringUtils;
65 import org.codehaus.plexus.util.xml.Xpp3Dom;
66
67 import java.io.File;
68 import java.lang.reflect.Method;
69 import java.net.MalformedURLException;
70 import java.net.URL;
71 import java.util.List;
72 import java.util.Locale;
73 import java.util.Set;
74
75
76
77
78
79
80
81
82 public abstract class AbstractDeployMojo
83 extends AbstractSiteMojo
84 implements Contextualizable
85 {
86
87
88
89 @Parameter( alias = "outputDirectory", property = "project.reporting.outputDirectory", required = true )
90 private File inputDirectory;
91
92
93
94
95
96
97
98 @Parameter( property = "maven.site.chmod", defaultValue = "true" )
99 private boolean chmod;
100
101
102
103
104
105
106
107 @Parameter( property = "maven.site.chmod.mode", defaultValue = "g+w,a+rX" )
108 private String chmodMode;
109
110
111
112
113
114
115
116 @Parameter( property = "maven.site.chmod.options", defaultValue = "-Rf" )
117 private String chmodOptions;
118
119
120
121
122
123
124 @Parameter( property = "maven.site.deploy.skip", defaultValue = "false" )
125
126 private boolean skipDeploy;
127
128
129
130 @Component
131 private WagonManager wagonManager;
132
133
134
135
136 @Component
137 private Settings settings;
138
139
140
141
142 @Component
143 protected MavenSession mavenSession;
144
145 private PlexusContainer container;
146
147
148
149
150 protected static final String DEFAULT_STAGING_DIRECTORY = "staging/";
151
152
153
154
155 public void execute()
156 throws MojoExecutionException
157 {
158 if ( skipDeploy && isDeploy() )
159 {
160 getLog().info( "maven.site.deploy.skip = true: Skipping site deployment" );
161 return;
162 }
163
164 deployTo( new org.apache.maven.plugins.site.wagon.repository.Repository( getDeployRepositoryID(), appendSlash(
165 getDeployRepositoryURL() ) ) );
166 }
167
168
169
170
171
172
173
174
175 protected static String appendSlash( final String url )
176 {
177 if ( url.endsWith( "/" ) )
178 {
179 return url;
180 }
181 else
182 {
183 return url + "/";
184 }
185 }
186
187
188
189
190
191
192
193 protected abstract String getDeployRepositoryID()
194 throws MojoExecutionException;
195
196
197
198
199
200
201
202
203 protected abstract String getDeployRepositoryURL()
204 throws MojoExecutionException;
205
206
207
208
209
210
211
212 protected String getDeployModuleDirectory()
213 throws MojoExecutionException
214 {
215 String relative = siteTool.getRelativePath( getSite( project ).getUrl(), getRootSite( project ).getUrl() );
216
217
218
219 relative = relative.replace( '\\', '/' );
220
221 return ( "".equals( relative ) ) ? "./" : relative;
222 }
223
224
225
226
227
228
229
230
231
232
233 private void deployTo( final Repository repository )
234 throws MojoExecutionException
235 {
236 if ( !inputDirectory.exists() )
237 {
238 throw new MojoExecutionException( "The site does not exist, please run site:site first" );
239 }
240
241 if ( getLog().isDebugEnabled() )
242 {
243 getLog().debug( "Deploying to '" + repository.getUrl() + "',\n Using credentials from server id '"
244 + repository.getId() + "'" );
245 }
246
247 deploy( inputDirectory, repository );
248 }
249
250 private void deploy( final File directory, final Repository repository )
251 throws MojoExecutionException
252 {
253
254 final Wagon wagon = getWagon( repository, wagonManager );
255
256 try
257 {
258 configureWagon( wagon, repository.getId(), settings, container, getLog() );
259 }
260 catch ( TransferFailedException e )
261 {
262 throw new MojoExecutionException( "Unable to configure Wagon: '" + repository.getProtocol() + "'", e );
263 }
264
265 try
266 {
267 final ProxyInfo proxyInfo;
268 if ( !isMaven3OrMore() )
269 {
270 proxyInfo = getProxyInfo( repository, wagonManager );
271 }
272 else
273 {
274 try
275 {
276 SettingsDecrypter settingsDecrypter = container.lookup( SettingsDecrypter.class );
277
278 proxyInfo = getProxy( repository, settingsDecrypter );
279 }
280 catch ( ComponentLookupException cle )
281 {
282 throw new MojoExecutionException( "Unable to lookup SettingsDecrypter: " + cle.getMessage(), cle );
283 }
284 }
285
286 push( directory, repository, wagon, proxyInfo, siteTool.getAvailableLocales( locales ),
287 getDeployModuleDirectory() );
288
289 if ( chmod )
290 {
291 chmod( wagon, repository, chmodOptions, chmodMode );
292 }
293 }
294 finally
295 {
296 try
297 {
298 wagon.disconnect();
299 }
300 catch ( ConnectionException e )
301 {
302 getLog().error( "Error disconnecting wagon - ignored", e );
303 }
304 }
305 }
306
307
308
309
310
311
312
313 protected File getTopLevelBuildDirectory()
314 {
315
316 final MavenProject topLevelProject = getTopLevelProject( reactorProjects );
317
318
319 final File buildDirectory;
320
321 if ( topLevelProject == null )
322 {
323 getLog().debug( "No top level project found in the reactor, using the current project." );
324
325 buildDirectory = new File( project.getBuild().getDirectory() );
326 }
327 else
328 {
329 getLog().debug( "Using the top level project found in the reactor." );
330
331 buildDirectory = new File( topLevelProject.getBuild().getDirectory() );
332 }
333
334 return buildDirectory;
335 }
336
337 private Wagon getWagon( final Repository repository, final WagonManager manager )
338 throws MojoExecutionException
339 {
340 final Wagon wagon;
341
342 try
343 {
344 wagon = manager.getWagon( repository );
345 }
346 catch ( UnsupportedProtocolException e )
347 {
348 String shortMessage = "Unsupported protocol: '" + repository.getProtocol() + "' for site deployment to "
349 + "distributionManagement.site.url=" + repository.getUrl() + ".";
350 String longMessage =
351 "\n" + shortMessage + "\n" + "Currently supported protocols are: " + getSupportedProtocols() + ".\n"
352 + " Protocols may be added through wagon providers.\n" + " For more information, see "
353 + "http://maven.apache.org/plugins/maven-site-plugin/examples/adding-deploy-protocol.html";
354
355 getLog().error( longMessage );
356
357 throw new MojoExecutionException( shortMessage );
358 }
359 catch ( TransferFailedException e )
360 {
361 throw new MojoExecutionException( "Unable to configure Wagon: '" + repository.getProtocol() + "'", e );
362 }
363
364 if ( !wagon.supportsDirectoryCopy() )
365 {
366 throw new MojoExecutionException(
367 "Wagon protocol '" + repository.getProtocol() + "' doesn't support directory copying" );
368 }
369
370 return wagon;
371 }
372
373 private String getSupportedProtocols()
374 {
375 try
376 {
377 Set<String> protocols = container.lookupMap( Wagon.class ).keySet();
378
379 return StringUtils.join( protocols.iterator(), ", " );
380 }
381 catch ( ComponentLookupException e )
382 {
383
384 getLog().error( e );
385 }
386 return "";
387 }
388
389 private void push( final File inputDirectory, final Repository repository, final Wagon wagon,
390 final ProxyInfo proxyInfo, final List<Locale> localesList, final String relativeDir )
391 throws MojoExecutionException
392 {
393 AuthenticationInfo authenticationInfo = wagonManager.getAuthenticationInfo( repository.getId() );
394 getLog().debug( "authenticationInfo with id '" + repository.getId() + "': "
395 + ( ( authenticationInfo == null ) ? "-" : authenticationInfo.getUserName() ) );
396
397 try
398 {
399 Debug debug = new Debug();
400
401 wagon.addSessionListener( debug );
402
403 wagon.addTransferListener( debug );
404
405 if ( proxyInfo != null )
406 {
407 getLog().debug( "connect with proxyInfo" );
408 wagon.connect( repository, authenticationInfo, proxyInfo );
409 }
410 else if ( proxyInfo == null && authenticationInfo != null )
411 {
412 getLog().debug( "connect with authenticationInfo and without proxyInfo" );
413 wagon.connect( repository, authenticationInfo );
414 }
415 else
416 {
417 getLog().debug( "connect without authenticationInfo and without proxyInfo" );
418 wagon.connect( repository );
419 }
420
421 getLog().info( "Pushing " + inputDirectory );
422
423
424 final String defaultLocale = localesList.get( 0 ).getLanguage();
425
426 for ( Locale locale : localesList )
427 {
428 if ( locale.getLanguage().equals( defaultLocale ) )
429 {
430
431
432 getLog().info( " >>> to " + repository.getUrl() + relativeDir );
433
434 wagon.putDirectory( inputDirectory, relativeDir );
435 }
436 else
437 {
438 getLog().info( " >>> to " + repository.getUrl() + locale.getLanguage() + "/" + relativeDir );
439
440 wagon.putDirectory( new File( inputDirectory, locale.getLanguage() ),
441 locale.getLanguage() + "/" + relativeDir );
442 }
443 }
444 }
445 catch ( ResourceDoesNotExistException e )
446 {
447 throw new MojoExecutionException( "Error uploading site", e );
448 }
449 catch ( TransferFailedException e )
450 {
451 throw new MojoExecutionException( "Error uploading site", e );
452 }
453 catch ( AuthorizationException e )
454 {
455 throw new MojoExecutionException( "Error uploading site", e );
456 }
457 catch ( ConnectionException e )
458 {
459 throw new MojoExecutionException( "Error uploading site", e );
460 }
461 catch ( AuthenticationException e )
462 {
463 throw new MojoExecutionException( "Error uploading site", e );
464 }
465 }
466
467 private static void chmod( final Wagon wagon, final Repository repository, final String chmodOptions,
468 final String chmodMode )
469 throws MojoExecutionException
470 {
471 try
472 {
473 if ( wagon instanceof CommandExecutor )
474 {
475 CommandExecutor exec = (CommandExecutor) wagon;
476 exec.executeCommand( "chmod " + chmodOptions + " " + chmodMode + " " + repository.getBasedir() );
477 }
478
479 }
480 catch ( CommandExecutionException e )
481 {
482 throw new MojoExecutionException( "Error uploading site", e );
483 }
484 }
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504 public static ProxyInfo getProxyInfo( Repository repository, WagonManager wagonManager )
505 {
506 ProxyInfo proxyInfo = wagonManager.getProxy( repository.getProtocol() );
507
508 if ( proxyInfo == null )
509 {
510 return null;
511 }
512
513 String host = repository.getHost();
514 String nonProxyHostsAsString = proxyInfo.getNonProxyHosts();
515 for ( String nonProxyHost : StringUtils.split( nonProxyHostsAsString, ",;|" ) )
516 {
517 if ( StringUtils.contains( nonProxyHost, "*" ) )
518 {
519
520 final int pos = nonProxyHost.indexOf( '*' );
521 String nonProxyHostPrefix = nonProxyHost.substring( 0, pos );
522 String nonProxyHostSuffix = nonProxyHost.substring( pos + 1 );
523
524 if ( StringUtils.isNotEmpty( nonProxyHostPrefix ) && host.startsWith( nonProxyHostPrefix )
525 && StringUtils.isEmpty( nonProxyHostSuffix ) )
526 {
527 return null;
528 }
529
530 if ( StringUtils.isEmpty( nonProxyHostPrefix ) && StringUtils.isNotEmpty( nonProxyHostSuffix )
531 && host.endsWith( nonProxyHostSuffix ) )
532 {
533 return null;
534 }
535
536 if ( StringUtils.isNotEmpty( nonProxyHostPrefix ) && host.startsWith( nonProxyHostPrefix )
537 && StringUtils.isNotEmpty( nonProxyHostSuffix ) && host.endsWith( nonProxyHostSuffix ) )
538 {
539 return null;
540 }
541 }
542 else if ( host.equals( nonProxyHost ) )
543 {
544 return null;
545 }
546 }
547 return proxyInfo;
548 }
549
550
551
552
553
554
555
556
557 private ProxyInfo getProxy( Repository repository, SettingsDecrypter settingsDecrypter )
558 {
559 String protocol = repository.getProtocol();
560 String url = repository.getUrl();
561
562 getLog().debug( "repository protocol " + protocol );
563
564 String originalProtocol = protocol;
565
566
567
568
569 if ( StringUtils.equalsIgnoreCase( "dav", protocol ) && url.startsWith( "dav:" ) )
570 {
571 url = url.substring( 4 );
572 if ( url.startsWith( "http" ) )
573 {
574 try
575 {
576 URL urlSite = new URL( url );
577 protocol = urlSite.getProtocol();
578 getLog().debug( "found dav protocol so transform to real transport protocol " + protocol );
579 }
580 catch ( MalformedURLException e )
581 {
582 getLog().warn( "fail to build URL with " + url );
583 }
584
585 }
586 }
587 else
588 {
589 getLog().debug( "getProxy 'protocol': " + protocol );
590 }
591 if ( mavenSession != null && protocol != null )
592 {
593 MavenExecutionRequest request = mavenSession.getRequest();
594
595 if ( request != null )
596 {
597 List<Proxy> proxies = request.getProxies();
598
599 if ( proxies != null )
600 {
601 for ( Proxy proxy : proxies )
602 {
603 if ( proxy.isActive() && ( protocol.equalsIgnoreCase( proxy.getProtocol() )
604 || originalProtocol.equalsIgnoreCase( proxy.getProtocol() ) ) )
605 {
606 SettingsDecryptionResult result =
607 settingsDecrypter.decrypt( new DefaultSettingsDecryptionRequest( proxy ) );
608 proxy = result.getProxy();
609
610 ProxyInfo proxyInfo = new ProxyInfo();
611 proxyInfo.setHost( proxy.getHost() );
612
613 proxyInfo.setType( protocol );
614 proxyInfo.setPort( proxy.getPort() );
615 proxyInfo.setNonProxyHosts( proxy.getNonProxyHosts() );
616 proxyInfo.setUserName( proxy.getUsername() );
617 proxyInfo.setPassword( proxy.getPassword() );
618
619 getLog().debug( "found proxyInfo " + ( proxyInfo == null
620 ? "null"
621 : "host:port " + proxyInfo.getHost() + ":" + proxyInfo.getPort() + ", "
622 + proxyInfo.getUserName() ) );
623
624 return proxyInfo;
625 }
626 }
627 }
628 }
629 }
630 getLog().debug( "getProxy 'protocol': " + protocol + " no ProxyInfo found" );
631 return null;
632 }
633
634
635
636
637
638
639
640
641
642
643
644
645 private static void configureWagon( Wagon wagon, String repositoryId, Settings settings, PlexusContainer container,
646 Log log )
647 throws TransferFailedException
648 {
649 log.debug( " configureWagon " );
650
651
652 for ( Server server : settings.getServers() )
653 {
654 String id = server.getId();
655
656 log.debug( "configureWagon server " + id );
657
658 if ( id != null && id.equals( repositoryId ) && ( server.getConfiguration() != null ) )
659 {
660 final PlexusConfiguration plexusConf =
661 new XmlPlexusConfiguration( (Xpp3Dom) server.getConfiguration() );
662
663 ComponentConfigurator componentConfigurator = null;
664 try
665 {
666 componentConfigurator =
667 (ComponentConfigurator) container.lookup( ComponentConfigurator.ROLE, "basic" );
668 if ( isMaven3OrMore() )
669 {
670 componentConfigurator.configureComponent( wagon, plexusConf,
671 container.getContainerRealm() );
672 }
673 else
674 {
675 configureWagonWithMaven2( componentConfigurator, wagon, plexusConf, container );
676 }
677 }
678 catch ( final ComponentLookupException e )
679 {
680 throw new TransferFailedException(
681 "While configuring wagon for \'" + repositoryId + "\': Unable to lookup wagon configurator."
682 + " Wagon configuration cannot be applied.", e );
683 }
684 catch ( ComponentConfigurationException e )
685 {
686 throw new TransferFailedException( "While configuring wagon for \'" + repositoryId
687 + "\': Unable to apply wagon configuration.", e );
688 }
689 finally
690 {
691 if ( componentConfigurator != null )
692 {
693 try
694 {
695 container.release( componentConfigurator );
696 }
697 catch ( ComponentLifecycleException e )
698 {
699 log.error( "Problem releasing configurator - ignoring: " + e.getMessage() );
700 }
701 }
702 }
703 }
704 }
705 }
706
707 private static void configureWagonWithMaven2( ComponentConfigurator componentConfigurator, Wagon wagon,
708 PlexusConfiguration plexusConf, PlexusContainer container )
709 throws ComponentConfigurationException
710 {
711
712
713
714
715 try
716 {
717 Method methodContainerRealm = container.getClass().getMethod( "getContainerRealm" );
718 ClassRealm realm = (ClassRealm) methodContainerRealm.invoke( container, null );
719
720 Method methodConfigure = componentConfigurator.getClass().getMethod( "configureComponent",
721 new Class[]{ Object.class,
722 PlexusConfiguration.class,
723 ClassRealm.class } );
724
725 methodConfigure.invoke( componentConfigurator, wagon, plexusConf, realm );
726 }
727 catch ( Exception e )
728 {
729 throw new ComponentConfigurationException(
730 "fail to configure wagon component for a maven2 use " + e.getMessage(), e );
731 }
732 }
733
734
735
736
737 public void contextualize( Context context )
738 throws ContextException
739 {
740 container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
741 }
742
743
744
745
746
747
748
749 private static MavenProject getTopLevelProject( List<MavenProject> reactorProjects )
750 {
751 if ( reactorProjects == null )
752 {
753 return null;
754 }
755
756 for ( MavenProject reactorProject : reactorProjects )
757 {
758 if ( reactorProject.isExecutionRoot() )
759 {
760 return reactorProject;
761 }
762 }
763
764 return null;
765 }
766
767
768
769
770
771
772
773
774
775 protected static Site getSite( final MavenProject project )
776 throws MojoExecutionException
777 {
778 final String name =
779 project.getName() + " (" + project.getGroupId() + ":" + project.getArtifactId() + ":" + project.getVersion()
780 + ")";
781
782 final DistributionManagement distributionManagement = project.getDistributionManagement();
783
784 if ( distributionManagement == null )
785 {
786 throw new MojoExecutionException( "Missing distribution management in project " + name );
787 }
788
789 final Site site = distributionManagement.getSite();
790
791 if ( site == null )
792 {
793 throw new MojoExecutionException(
794 "Missing site information in the distribution management of the project " + name );
795 }
796
797 if ( site.getUrl() == null || site.getId() == null )
798 {
799 throw new MojoExecutionException( "Missing site data: specify url and id for project " + name );
800 }
801
802 return site;
803 }
804
805
806
807
808
809
810
811
812
813
814
815 protected Site getRootSite( MavenProject project )
816 throws MojoExecutionException
817 {
818 Site site = getSite( project );
819
820 MavenProject parent = project;
821
822 while ( parent.getParent() != null )
823 {
824
825 parent = siteTool.getParentProject( parent, reactorProjects, localRepository );
826
827 Site oldSite = site;
828
829 try
830 {
831 site = getSite( parent );
832 }
833 catch ( MojoExecutionException e )
834 {
835 break;
836 }
837
838
839 URIPathDescriptor siteURI = new URIPathDescriptor( URIEncoder.encodeURI( site.getUrl() ), "" );
840 URIPathDescriptor oldSiteURI = new URIPathDescriptor( URIEncoder.encodeURI( oldSite.getUrl() ), "" );
841
842 if ( !siteURI.sameSite( oldSiteURI.getBaseURI() ) )
843 {
844 return oldSite;
845 }
846 }
847
848 return site;
849 }
850
851
852
853
854
855
856 protected boolean isDeploy()
857 {
858 return true;
859 }
860
861 private static class URIEncoder
862 {
863 private static final String MARK = "-_.!~*'()";
864 private static final String RESERVED = ";/?:@&=+$,";
865
866 public static String encodeURI( final String uriString )
867 {
868 final char[] chars = uriString.toCharArray();
869 final StringBuilder uri = new StringBuilder( chars.length );
870
871 for ( char c : chars )
872 {
873 if ( ( c >= '0' && c <= '9' ) || ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' )
874 || MARK.indexOf( c ) != -1 || RESERVED.indexOf( c ) != -1 )
875 {
876 uri.append( c );
877 }
878 else
879 {
880 uri.append( '%' );
881 uri.append( Integer.toHexString( (int) c ) );
882 }
883 }
884 return uri.toString();
885 }
886 }
887 }