1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.site.deploy;
20
21 import java.io.File;
22 import java.net.MalformedURLException;
23 import java.net.URL;
24 import java.util.List;
25 import java.util.Locale;
26
27 import org.apache.maven.doxia.site.inheritance.URIPathDescriptor;
28 import org.apache.maven.doxia.tools.SiteTool;
29 import org.apache.maven.execution.MavenExecutionRequest;
30 import org.apache.maven.execution.MavenSession;
31 import org.apache.maven.model.DistributionManagement;
32 import org.apache.maven.model.Site;
33 import org.apache.maven.plugin.MojoExecutionException;
34 import org.apache.maven.plugins.annotations.Component;
35 import org.apache.maven.plugins.annotations.Parameter;
36 import org.apache.maven.plugins.site.AbstractSiteMojo;
37 import org.apache.maven.project.MavenProject;
38 import org.apache.maven.settings.Proxy;
39 import org.apache.maven.settings.Server;
40 import org.apache.maven.settings.Settings;
41 import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
42 import org.apache.maven.settings.crypto.SettingsDecrypter;
43 import org.apache.maven.settings.crypto.SettingsDecryptionResult;
44 import org.apache.maven.wagon.CommandExecutionException;
45 import org.apache.maven.wagon.CommandExecutor;
46 import org.apache.maven.wagon.ConnectionException;
47 import org.apache.maven.wagon.ResourceDoesNotExistException;
48 import org.apache.maven.wagon.TransferFailedException;
49 import org.apache.maven.wagon.Wagon;
50 import org.apache.maven.wagon.authentication.AuthenticationException;
51 import org.apache.maven.wagon.authentication.AuthenticationInfo;
52 import org.apache.maven.wagon.authorization.AuthorizationException;
53 import org.apache.maven.wagon.observers.Debug;
54 import org.apache.maven.wagon.proxy.ProxyInfo;
55 import org.apache.maven.wagon.repository.Repository;
56 import org.codehaus.plexus.PlexusContainer;
57 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
58
59
60
61
62
63
64
65
66 public abstract class AbstractDeployMojo extends AbstractSiteMojo {
67
68
69
70
71
72 @Parameter(alias = "outputDirectory", defaultValue = "${project.reporting.outputDirectory}", required = true)
73 private File inputDirectory;
74
75
76
77
78
79
80
81 @Parameter(property = "maven.site.chmod", defaultValue = "true")
82 private boolean chmod;
83
84
85
86
87
88
89
90 @Parameter(property = "maven.site.chmod.mode", defaultValue = "g+w,a+rX")
91 private String chmodMode;
92
93
94
95
96
97
98
99 @Parameter(property = "maven.site.chmod.options", defaultValue = "-Rf")
100 private String chmodOptions;
101
102
103
104
105
106
107 @Parameter(property = "maven.site.deploy.skip", defaultValue = "false")
108 private boolean skipDeploy;
109
110
111
112
113 @Parameter(defaultValue = "${settings}", readonly = true)
114 private Settings settings;
115
116
117
118
119 @Parameter(defaultValue = "${session}", readonly = true)
120 protected MavenSession mavenSession;
121
122 private String topDistributionManagementSiteUrl;
123
124 private Site deploySite;
125
126 @Component
127 private PlexusContainer container;
128
129 @Component
130 SettingsDecrypter settingsDecrypter;
131
132
133
134
135 public void execute() throws MojoExecutionException {
136 if (skip && isDeploy()) {
137 getLog().info("maven.site.skip = true: Skipping site deployment");
138 return;
139 }
140
141 if (skipDeploy && isDeploy()) {
142 getLog().info("maven.site.deploy.skip = true: Skipping site deployment");
143 return;
144 }
145
146 deployTo(new Repository(getDeploySite().getId(), getDeploySite().getUrl()));
147 }
148
149
150
151
152
153
154
155
156 protected static String appendSlash(final String url) {
157 if (url.endsWith("/")) {
158 return url;
159 } else {
160 return url + "/";
161 }
162 }
163
164
165
166
167
168
169 protected abstract boolean isDeploy();
170
171
172
173
174
175
176
177
178
179
180 protected String getTopDistributionManagementSiteUrl() throws MojoExecutionException {
181 if (topDistributionManagementSiteUrl == null) {
182 topDistributionManagementSiteUrl = determineTopDistributionManagementSiteUrl();
183
184 if (!isDeploy()) {
185 getLog().debug("distributionManagement.site.url relative path: " + getDeployModuleDirectory());
186 }
187 }
188 return topDistributionManagementSiteUrl;
189 }
190
191 protected abstract String determineTopDistributionManagementSiteUrl() throws MojoExecutionException;
192
193
194
195
196
197
198
199
200
201
202 protected Site getDeploySite() throws MojoExecutionException {
203 if (deploySite == null) {
204 deploySite = determineDeploySite();
205 }
206 return deploySite;
207 }
208
209 protected abstract Site determineDeploySite() throws MojoExecutionException;
210
211
212
213
214
215
216
217 protected String getDeployModuleDirectory() throws MojoExecutionException {
218 String to = getSite(project).getUrl();
219
220 getLog().debug("Mapping url source calculation: ");
221 String from = getTopDistributionManagementSiteUrl();
222
223 String relative = siteTool.getRelativePath(to, from);
224
225
226
227 relative = relative.replace('\\', '/');
228
229 return ("".equals(relative)) ? "./" : relative;
230 }
231
232
233
234
235
236
237
238
239
240
241 private void deployTo(final Repository repository) throws MojoExecutionException {
242 if (!inputDirectory.exists()) {
243 throw new MojoExecutionException("The site does not exist, please run site:site first");
244 }
245
246 if (getLog().isDebugEnabled()) {
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) throws MojoExecutionException {
255
256 final Wagon wagon = getWagon(repository);
257
258 try {
259 ProxyInfo proxyInfo = getProxy(repository, settingsDecrypter);
260
261 push(directory, repository, wagon, proxyInfo, getLocales(), getDeployModuleDirectory());
262
263 if (chmod) {
264 chmod(wagon, repository, chmodOptions, chmodMode);
265 }
266 } finally {
267 try {
268 wagon.disconnect();
269 } catch (ConnectionException e) {
270 getLog().error("Error disconnecting wagon - ignored", e);
271 }
272 }
273 }
274
275 private Wagon getWagon(final Repository repository) throws MojoExecutionException {
276 String protocol = repository.getProtocol();
277 if (protocol == null) {
278 throw new MojoExecutionException("Unspecified protocol");
279 }
280 try {
281 Wagon wagon = container.lookup(Wagon.class, protocol.toLowerCase(Locale.ROOT));
282 if (!wagon.supportsDirectoryCopy()) {
283 throw new MojoExecutionException(
284 "Wagon protocol '" + repository.getProtocol() + "' doesn't support directory copying");
285 }
286 return wagon;
287 } catch (ComponentLookupException e) {
288 throw new MojoExecutionException("Cannot find wagon which supports the requested protocol: " + protocol, e);
289 }
290 }
291
292 public AuthenticationInfo getAuthenticationInfo(String id) {
293 if (id != null) {
294 List<Server> servers = settings.getServers();
295
296 if (servers != null) {
297 for (Server server : servers) {
298 if (id.equalsIgnoreCase(server.getId())) {
299 SettingsDecryptionResult result =
300 settingsDecrypter.decrypt(new DefaultSettingsDecryptionRequest(server));
301 server = result.getServer();
302
303 AuthenticationInfo authInfo = new AuthenticationInfo();
304 authInfo.setUserName(server.getUsername());
305 authInfo.setPassword(server.getPassword());
306 authInfo.setPrivateKey(server.getPrivateKey());
307 authInfo.setPassphrase(server.getPassphrase());
308
309 return authInfo;
310 }
311 }
312 }
313 }
314
315 return null;
316 }
317
318 private void push(
319 final File inputDirectory,
320 final Repository repository,
321 final Wagon wagon,
322 final ProxyInfo proxyInfo,
323 final List<Locale> localesList,
324 final String relativeDir)
325 throws MojoExecutionException {
326 AuthenticationInfo authenticationInfo = getAuthenticationInfo(repository.getId());
327 if (authenticationInfo != null) {
328 getLog().debug("authenticationInfo with id '" + repository.getId() + "'");
329 }
330
331 try {
332 if (getLog().isDebugEnabled()) {
333 Debug debug = new Debug();
334
335 wagon.addSessionListener(debug);
336
337 wagon.addTransferListener(debug);
338 }
339
340 if (proxyInfo != null) {
341 getLog().debug("connect with proxyInfo");
342 wagon.connect(repository, authenticationInfo, proxyInfo);
343 } else if (authenticationInfo != null) {
344 getLog().debug("connect with authenticationInfo and without proxyInfo");
345 wagon.connect(repository, authenticationInfo);
346 } else {
347 getLog().debug("connect without authenticationInfo and without proxyInfo");
348 wagon.connect(repository);
349 }
350
351 getLog().info("Pushing " + inputDirectory);
352
353 for (Locale locale : localesList) {
354 if (!locale.equals(SiteTool.DEFAULT_LOCALE)) {
355 getLog().info(" >>> to " + appendSlash(repository.getUrl()) + locale + "/" + relativeDir);
356
357 wagon.putDirectory(new File(inputDirectory, locale.toString()), locale + "/" + relativeDir);
358 } else {
359
360
361 getLog().info(" >>> to " + appendSlash(repository.getUrl()) + relativeDir);
362
363 wagon.putDirectory(inputDirectory, relativeDir);
364 }
365 }
366 } catch (ResourceDoesNotExistException
367 | TransferFailedException
368 | AuthorizationException
369 | ConnectionException
370 | AuthenticationException e) {
371 throw new MojoExecutionException("Error uploading site", e);
372 }
373 }
374
375 private static void chmod(
376 final Wagon wagon, final Repository repository, final String chmodOptions, final String chmodMode)
377 throws MojoExecutionException {
378 try {
379 if (wagon instanceof CommandExecutor) {
380 CommandExecutor exec = (CommandExecutor) wagon;
381 exec.executeCommand("chmod " + chmodOptions + " " + chmodMode + " " + repository.getBasedir());
382 }
383
384 } catch (CommandExecutionException e) {
385 throw new MojoExecutionException("Error uploading site", e);
386 }
387 }
388
389
390
391
392
393
394
395
396 private ProxyInfo getProxy(Repository repository, SettingsDecrypter settingsDecrypter) {
397 String protocol = repository.getProtocol();
398 String url = repository.getUrl();
399
400 getLog().debug("repository protocol " + protocol);
401
402 String originalProtocol = protocol;
403
404
405
406
407 if ("dav".equalsIgnoreCase(protocol) && url.startsWith("dav:")) {
408 url = url.substring(4);
409 if (url.startsWith("http")) {
410 try {
411 URL urlSite = new URL(url);
412 protocol = urlSite.getProtocol();
413 getLog().debug("found dav protocol so transform to real transport protocol " + protocol);
414 } catch (MalformedURLException e) {
415 getLog().warn("fail to build URL with " + url);
416 }
417 }
418 } else {
419 getLog().debug("getProxy 'protocol': " + protocol);
420 }
421
422 if (mavenSession != null && protocol != null) {
423 MavenExecutionRequest request = mavenSession.getRequest();
424
425 if (request != null) {
426 List<Proxy> proxies = request.getProxies();
427
428 if (proxies != null) {
429 for (Proxy proxy : proxies) {
430 if (proxy.isActive()
431 && (protocol.equalsIgnoreCase(proxy.getProtocol())
432 || originalProtocol.equalsIgnoreCase(proxy.getProtocol()))) {
433 SettingsDecryptionResult result =
434 settingsDecrypter.decrypt(new DefaultSettingsDecryptionRequest(proxy));
435 proxy = result.getProxy();
436
437 ProxyInfo proxyInfo = new ProxyInfo();
438 proxyInfo.setHost(proxy.getHost());
439
440
441 proxyInfo.setType(protocol);
442 proxyInfo.setPort(proxy.getPort());
443 proxyInfo.setNonProxyHosts(proxy.getNonProxyHosts());
444 proxyInfo.setUserName(proxy.getUsername());
445 proxyInfo.setPassword(proxy.getPassword());
446
447 getLog().debug("found proxyInfo "
448 + ("host:port " + proxyInfo.getHost() + ":" + proxyInfo.getPort() + ", "
449 + proxyInfo.getUserName()));
450
451 return proxyInfo;
452 }
453 }
454 }
455 }
456 }
457 getLog().debug("getProxy 'protocol': " + protocol + " no ProxyInfo found");
458 return null;
459 }
460
461
462
463
464
465
466
467
468
469 protected static Site getSite(final MavenProject project) throws MojoExecutionException {
470 final DistributionManagement distributionManagement = project.getDistributionManagement();
471
472 if (distributionManagement == null) {
473 throw new MojoExecutionException("Missing distribution management in project " + getFullName(project));
474 }
475
476 final Site site = distributionManagement.getSite();
477
478 if (site == null) {
479 throw new MojoExecutionException(
480 "Missing site information in the distribution management of the project " + getFullName(project));
481 }
482
483 if (site.getUrl() == null || site.getId() == null) {
484 throw new MojoExecutionException(
485 "Missing site data: specify url and id for project " + getFullName(project));
486 }
487
488 return site;
489 }
490
491 private static String getFullName(MavenProject project) {
492 return project.getName() + " (" + project.getGroupId() + ':' + project.getArtifactId() + ':'
493 + project.getVersion() + ')';
494 }
495
496
497
498
499
500
501
502
503
504
505
506
507
508 protected MavenProject getTopLevelProject(MavenProject project) throws MojoExecutionException {
509 Site site = getSite(project);
510
511 MavenProject parent = project;
512
513 while (parent.getParent() != null) {
514 MavenProject oldProject = parent;
515
516 parent = parent.getParent();
517
518 Site oldSite = site;
519
520 try {
521 site = getSite(parent);
522 } catch (MojoExecutionException e) {
523 return oldProject;
524 }
525
526
527 URIPathDescriptor siteURI = new URIPathDescriptor(URIEncoder.encodeURI(site.getUrl()), "");
528 URIPathDescriptor oldSiteURI = new URIPathDescriptor(URIEncoder.encodeURI(oldSite.getUrl()), "");
529
530 if (!siteURI.sameSite(oldSiteURI.getBaseURI())) {
531 return oldProject;
532 }
533 }
534
535 return parent;
536 }
537
538 private static class URIEncoder {
539 private static final String MARK = "-_.!~*'()";
540 private static final String RESERVED = ";/?:@&=+$,";
541
542 private static String encodeURI(final String uriString) {
543 final char[] chars = uriString.toCharArray();
544 final StringBuilder uri = new StringBuilder(chars.length);
545
546
547 if (uriString.startsWith("dav:http")) {
548
549 chars[3] = '-';
550 }
551
552 for (char c : chars) {
553 if ((c >= '0' && c <= '9')
554 || (c >= 'a' && c <= 'z')
555 || (c >= 'A' && c <= 'Z')
556 || MARK.indexOf(c) != -1
557 || RESERVED.indexOf(c) != -1) {
558 uri.append(c);
559 } else {
560 uri.append('%');
561 uri.append(Integer.toHexString((int) c));
562 }
563 }
564 return uri.toString();
565 }
566 }
567 }