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