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 java.io.File;
23
24 import java.util.List;
25 import java.util.Locale;
26
27 import org.apache.maven.artifact.manager.WagonConfigurationException;
28 import org.apache.maven.artifact.manager.WagonManager;
29 import org.apache.maven.doxia.site.decoration.inheritance.URIPathDescriptor;
30 import org.apache.maven.model.DistributionManagement;
31 import org.apache.maven.model.Site;
32 import org.apache.maven.plugin.MojoExecutionException;
33 import org.apache.maven.plugin.logging.Log;
34 import org.apache.maven.project.MavenProject;
35 import org.apache.maven.settings.Server;
36 import org.apache.maven.settings.Settings;
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
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
66
67
68
69
70
71
72
73 public abstract class AbstractDeployMojo
74 extends AbstractSiteMojo implements Contextualizable
75 {
76
77
78
79
80
81
82 private File inputDirectory;
83
84
85
86
87
88
89
90
91 private boolean chmod;
92
93
94
95
96
97
98
99
100 private String chmodMode;
101
102
103
104
105
106
107
108
109 private String chmodOptions;
110
111
112
113
114
115
116
117 private boolean skipDeploy;
118
119
120
121
122 private WagonManager wagonManager;
123
124
125
126
127
128
129
130
131 private Settings settings;
132
133 private PlexusContainer container;
134
135
136
137
138 protected static final String DEFAULT_STAGING_DIRECTORY = "staging/";
139
140
141 public void execute()
142 throws MojoExecutionException
143 {
144 if ( skipDeploy )
145 {
146 getLog().info( "maven.site.deploy.skip = true: Skipping site deployment" );
147 return;
148 }
149
150 deployTo( new org.apache.maven.plugins.site.wagon.repository.Repository(
151 getDeployRepositoryID(),
152 appendSlash( getDeployRepositoryURL() ) ) );
153 }
154
155
156
157
158
159
160
161
162
163 protected static String appendSlash( final String url )
164 {
165 if ( url.endsWith( "/" ) )
166 {
167 return url;
168 }
169 else
170 {
171 return url + "/";
172 }
173 }
174
175
176
177
178
179
180
181
182
183 protected abstract String getDeployRepositoryID()
184 throws MojoExecutionException;
185
186
187
188
189
190
191
192
193
194
195 protected abstract String getDeployRepositoryURL()
196 throws MojoExecutionException;
197
198
199
200
201
202
203
204
205 private String getDeployModuleDirectory()
206 throws MojoExecutionException
207 {
208 String relative = siteTool.getRelativePath( getSite( project ).getUrl(),
209 getRootSite( project ).getUrl() );
210
211
212
213 relative = relative.replace( '\\', '/' );
214
215 return ( "".equals( relative ) ) ? "./" : relative;
216 }
217
218
219
220
221
222
223
224
225
226
227
228 private void deployTo( final Repository repository )
229 throws MojoExecutionException
230 {
231 if ( !inputDirectory.exists() )
232 {
233 throw new MojoExecutionException( "The site does not exist, please run site:site first" );
234 }
235
236 if ( getLog().isDebugEnabled() )
237 {
238 getLog().debug( "Deploying to '" + repository.getUrl()
239 + "',\n Using credentials from server id '" + repository.getId() + "'" );
240 }
241
242 deploy( inputDirectory, repository );
243 }
244
245 private void deploy( final File directory, final Repository repository )
246 throws MojoExecutionException
247 {
248
249 final Wagon wagon = getWagon( repository, wagonManager );
250
251 try
252 {
253 configureWagon( wagon, repository.getId(), settings, container, getLog() );
254 }
255 catch ( WagonConfigurationException e )
256 {
257 throw new MojoExecutionException( "Unable to configure Wagon: '" + repository.getProtocol() + "'", e );
258 }
259
260 try
261 {
262 final ProxyInfo proxyInfo = getProxyInfo( repository, wagonManager );
263
264 push( directory, repository, wagonManager, wagon, proxyInfo,
265 siteTool.getAvailableLocales( locales ), getDeployModuleDirectory(), getLog() );
266
267 if ( chmod )
268 {
269 chmod( wagon, repository, chmodOptions, chmodMode );
270 }
271 }
272 finally
273 {
274 try
275 {
276 wagon.disconnect();
277 }
278 catch ( ConnectionException e )
279 {
280 getLog().error( "Error disconnecting wagon - ignored", e );
281 }
282 }
283 }
284
285
286
287
288
289
290
291 protected File getTopLevelBuildDirectory()
292 {
293
294 final MavenProject topLevelProject = getTopLevelProject( reactorProjects );
295
296
297 final File buildDirectory;
298
299 if ( topLevelProject == null )
300 {
301 getLog().debug( "No top level project found in the reactor, using the current project." );
302
303 buildDirectory = new File( project.getBuild().getDirectory() );
304 }
305 else
306 {
307 getLog().debug( "Using the top level project found in the reactor." );
308
309 buildDirectory = new File( topLevelProject.getBuild().getDirectory() );
310 }
311
312 return buildDirectory;
313 }
314
315 private static Wagon getWagon( final Repository repository, final WagonManager manager )
316 throws MojoExecutionException
317 {
318 final Wagon wagon;
319
320 try
321 {
322 wagon = manager.getWagon( repository );
323 }
324 catch ( UnsupportedProtocolException e )
325 {
326 throw new MojoExecutionException( "Unsupported protocol: '" + repository.getProtocol() + "'", e );
327 }
328 catch ( WagonConfigurationException e )
329 {
330 throw new MojoExecutionException( "Unable to configure Wagon: '" + repository.getProtocol() + "'", e );
331 }
332
333 if ( !wagon.supportsDirectoryCopy() )
334 {
335 throw new MojoExecutionException(
336 "Wagon protocol '" + repository.getProtocol() + "' doesn't support directory copying" );
337 }
338
339 return wagon;
340 }
341
342 private static void push( final File inputDirectory, final Repository repository, final WagonManager manager,
343 final Wagon wagon, final ProxyInfo proxyInfo, final List<Locale> localesList,
344 final String relativeDir, final Log log )
345 throws MojoExecutionException
346 {
347 AuthenticationInfo authenticationInfo = manager.getAuthenticationInfo( repository.getId() );
348 log.debug( "authenticationInfo with id '" + repository.getId() + "': "
349 + ( ( authenticationInfo == null ) ? "-" : authenticationInfo.getUserName() ) );
350
351 try
352 {
353 Debug debug = new Debug();
354
355 wagon.addSessionListener( debug );
356
357 wagon.addTransferListener( debug );
358
359 if ( proxyInfo != null )
360 {
361 log.debug( "connect with proxyInfo" );
362 wagon.connect( repository, authenticationInfo, proxyInfo );
363 }
364 else if ( proxyInfo == null && authenticationInfo != null )
365 {
366 log.debug( "connect with authenticationInfo and without proxyInfo" );
367 wagon.connect( repository, authenticationInfo );
368 }
369 else
370 {
371 log.debug( "connect without authenticationInfo and without proxyInfo" );
372 wagon.connect( repository );
373 }
374
375 log.info( "Pushing " + inputDirectory );
376
377
378 final String defaultLocale = localesList.get( 0 ).getLanguage();
379
380 for ( Locale locale : localesList )
381 {
382 if ( locale.getLanguage().equals( defaultLocale ) )
383 {
384
385
386 log.info( " >>> to " + repository.getUrl() + relativeDir );
387
388 wagon.putDirectory( inputDirectory, relativeDir );
389 }
390 else
391 {
392 log.info( " >>> to " + repository.getUrl() + locale.getLanguage() + "/" + relativeDir );
393
394 wagon.putDirectory( new File( inputDirectory, locale.getLanguage() ),
395 locale.getLanguage() + "/" + relativeDir );
396 }
397 }
398 }
399 catch ( ResourceDoesNotExistException e )
400 {
401 throw new MojoExecutionException( "Error uploading site", e );
402 }
403 catch ( TransferFailedException e )
404 {
405 throw new MojoExecutionException( "Error uploading site", e );
406 }
407 catch ( AuthorizationException e )
408 {
409 throw new MojoExecutionException( "Error uploading site", e );
410 }
411 catch ( ConnectionException e )
412 {
413 throw new MojoExecutionException( "Error uploading site", e );
414 }
415 catch ( AuthenticationException e )
416 {
417 throw new MojoExecutionException( "Error uploading site", e );
418 }
419 }
420
421 private static void chmod( final Wagon wagon, final Repository repository,
422 final String chmodOptions, final String chmodMode )
423 throws MojoExecutionException
424 {
425 try
426 {
427 if ( wagon instanceof CommandExecutor )
428 {
429 CommandExecutor exec = (CommandExecutor) wagon;
430 exec.executeCommand( "chmod " + chmodOptions + " " + chmodMode + " " + repository.getBasedir() );
431 }
432
433 }
434 catch ( CommandExecutionException e )
435 {
436 throw new MojoExecutionException( "Error uploading site", e );
437 }
438 }
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458 public static ProxyInfo getProxyInfo( Repository repository, WagonManager wagonManager )
459 {
460 ProxyInfo proxyInfo = wagonManager.getProxy( repository.getProtocol() );
461
462 if ( proxyInfo == null )
463 {
464 return null;
465 }
466
467 String host = repository.getHost();
468 String nonProxyHostsAsString = proxyInfo.getNonProxyHosts();
469 for ( String nonProxyHost : StringUtils.split( nonProxyHostsAsString, ",;|" ) )
470 {
471 if ( StringUtils.contains( nonProxyHost, "*" ) )
472 {
473
474 final int pos = nonProxyHost.indexOf( '*' );
475 String nonProxyHostPrefix = nonProxyHost.substring( 0, pos );
476 String nonProxyHostSuffix = nonProxyHost.substring( pos + 1 );
477
478 if ( StringUtils.isNotEmpty( nonProxyHostPrefix ) && host.startsWith( nonProxyHostPrefix )
479 && StringUtils.isEmpty( nonProxyHostSuffix ) )
480 {
481 return null;
482 }
483
484 if ( StringUtils.isEmpty( nonProxyHostPrefix )
485 && StringUtils.isNotEmpty( nonProxyHostSuffix ) && host.endsWith( nonProxyHostSuffix ) )
486 {
487 return null;
488 }
489
490 if ( StringUtils.isNotEmpty( nonProxyHostPrefix ) && host.startsWith( nonProxyHostPrefix )
491 && StringUtils.isNotEmpty( nonProxyHostSuffix ) && host.endsWith( nonProxyHostSuffix ) )
492 {
493 return null;
494 }
495 }
496 else if ( host.equals( nonProxyHost ) )
497 {
498 return null;
499 }
500 }
501 return proxyInfo;
502 }
503
504
505
506
507
508
509
510
511
512
513
514
515 private static void configureWagon( Wagon wagon, String repositoryId, Settings settings, PlexusContainer container,
516 Log log )
517 throws WagonConfigurationException
518 {
519 log.debug( " configureWagon " );
520
521
522 for ( Server server : settings.getServers() )
523 {
524 String id = server.getId();
525
526 log.debug( "configureWagon server " + id );
527
528 if ( id != null && id.equals( repositoryId ) && ( server.getConfiguration() != null ) )
529 {
530 final PlexusConfiguration plexusConf =
531 new XmlPlexusConfiguration( (Xpp3Dom) server.getConfiguration() );
532
533 ComponentConfigurator componentConfigurator = null;
534 try
535 {
536 componentConfigurator = (ComponentConfigurator) container.lookup( ComponentConfigurator.ROLE );
537 componentConfigurator.configureComponent( wagon, plexusConf, container.getContainerRealm() );
538 }
539 catch ( final ComponentLookupException e )
540 {
541 throw new WagonConfigurationException( repositoryId, "Unable to lookup wagon configurator."
542 + " Wagon configuration cannot be applied.", e );
543 }
544 catch ( ComponentConfigurationException e )
545 {
546 throw new WagonConfigurationException( repositoryId, "Unable to apply wagon configuration.", e );
547 }
548 finally
549 {
550 if ( componentConfigurator != null )
551 {
552 try
553 {
554 container.release( componentConfigurator );
555 }
556 catch ( ComponentLifecycleException e )
557 {
558 log.error( "Problem releasing configurator - ignoring: " + e.getMessage() );
559 }
560 }
561 }
562 }
563 }
564 }
565
566
567 public void contextualize( Context context )
568 throws ContextException
569 {
570 container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
571 }
572
573
574
575
576
577
578
579
580 private static MavenProject getTopLevelProject( List<MavenProject> reactorProjects )
581 {
582 if ( reactorProjects == null )
583 {
584 return null;
585 }
586
587 for ( MavenProject reactorProject : reactorProjects )
588 {
589 if ( reactorProject.isExecutionRoot() )
590 {
591 return reactorProject;
592 }
593 }
594
595 return null;
596 }
597
598
599
600
601
602
603
604
605
606
607
608 protected static Site getSite( final MavenProject project )
609 throws MojoExecutionException
610 {
611 final String name = project.getName() + " ("
612 + project.getGroupId() + ":" + project.getArtifactId() + ":" + project.getVersion() + ")";
613
614 final DistributionManagement distributionManagement = project.getDistributionManagement();
615
616 if ( distributionManagement == null )
617 {
618 throw new MojoExecutionException( "Missing distribution management in project " + name );
619 }
620
621 final Site site = distributionManagement.getSite();
622
623 if ( site == null )
624 {
625 throw new MojoExecutionException(
626 "Missing site information in the distribution management of the project " + name );
627 }
628
629 if ( site.getUrl() == null || site.getId() == null )
630 {
631 throw new MojoExecutionException( "Missing site data: specify url and id for project " + name );
632 }
633
634 return site;
635 }
636
637
638
639
640
641
642
643
644
645
646
647
648
649 protected Site getRootSite( MavenProject project )
650 throws MojoExecutionException
651 {
652 Site site = getSite( project );
653
654 MavenProject parent = project;
655
656 while ( parent.getParent() != null )
657 {
658
659 parent = siteTool.getParentProject( parent, reactorProjects, localRepository );
660
661 Site oldSite = site;
662
663 try
664 {
665 site = getSite( parent );
666 }
667 catch ( MojoExecutionException e )
668 {
669 break;
670 }
671
672
673 URIPathDescriptor siteURI = new URIPathDescriptor( URIEncoder.encodeURI( site.getUrl() ), "" );
674 URIPathDescriptor oldSiteURI = new URIPathDescriptor( URIEncoder.encodeURI( oldSite.getUrl() ), "" );
675
676 if ( !siteURI.sameSite( oldSiteURI.getBaseURI() ) )
677 {
678 return oldSite;
679 }
680 }
681
682 return site;
683 }
684
685 private static class URIEncoder
686 {
687 private static final String mark = "-_.!~*'()";
688 private static final String reserved = ";/?:@&=+$,";
689
690 public static String encodeURI( final String uriString )
691 {
692 final char[] chars = uriString.toCharArray();
693 final StringBuilder uri = new StringBuilder( chars.length );
694
695 for ( int i = 0; i < chars.length; i++ )
696 {
697 final char c = chars[i];
698 if ( ( c >= '0' && c <= '9' ) || ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' )
699 || mark.indexOf( c ) != -1 || reserved.indexOf( c ) != -1 )
700 {
701 uri.append( c );
702 }
703 else
704 {
705 uri.append( '%' );
706 uri.append( Integer.toHexString( (int) c ) );
707 }
708 }
709 return uri.toString();
710 }
711 }
712 }