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