View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.scm.plugin;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Map.Entry;
28  import java.util.Objects;
29  import java.util.Properties;
30  
31  import org.apache.commons.lang3.StringUtils;
32  import org.apache.maven.plugin.AbstractMojo;
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.scm.ScmBranch;
37  import org.apache.maven.scm.ScmException;
38  import org.apache.maven.scm.ScmFileSet;
39  import org.apache.maven.scm.ScmResult;
40  import org.apache.maven.scm.ScmRevision;
41  import org.apache.maven.scm.ScmTag;
42  import org.apache.maven.scm.ScmVersion;
43  import org.apache.maven.scm.manager.ScmManager;
44  import org.apache.maven.scm.provider.ScmProviderRepository;
45  import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
46  import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
47  import org.apache.maven.scm.repository.ScmRepository;
48  import org.apache.maven.scm.repository.ScmRepositoryException;
49  import org.apache.maven.settings.Server;
50  import org.apache.maven.settings.Settings;
51  import org.apache.maven.settings.building.SettingsProblem;
52  import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
53  import org.apache.maven.settings.crypto.SettingsDecrypter;
54  import org.apache.maven.settings.crypto.SettingsDecryptionResult;
55  import org.apache.maven.shared.model.fileset.FileSet;
56  import org.apache.maven.shared.model.fileset.util.FileSetManager;
57  
58  /**
59   * @author <a href="evenisse@apache.org">Emmanuel Venisse</a>
60   * @author Olivier Lamy
61   */
62  public abstract class AbstractScmMojo extends AbstractMojo {
63  
64      protected static final String VERSION_TYPE_BRANCH = "branch";
65  
66      protected static final String VERSION_TYPE_REVISION = "revision";
67  
68      protected static final String VERSION_TYPE_TAG = "tag";
69  
70      protected static final String[] VALID_VERSION_TYPES = {VERSION_TYPE_BRANCH, VERSION_TYPE_REVISION, VERSION_TYPE_TAG
71      };
72  
73      /**
74       * The SCM connection URL.
75       */
76      @Parameter(property = "connectionUrl", defaultValue = "${project.scm.connection}")
77      private String connectionUrl;
78  
79      /**
80       * The SCM connection URL for developers.
81       */
82      @Parameter(property = "developerConnectionUrl", defaultValue = "${project.scm.developerConnection}")
83      private String developerConnectionUrl;
84  
85      /**
86       * The type of connection to use (connection or developerConnection).
87       */
88      @Parameter(property = "connectionType", defaultValue = "connection")
89      private String connectionType;
90  
91      /**
92       * The working directory.
93       */
94      @Parameter(property = "workingDirectory")
95      private File workingDirectory;
96  
97      /**
98       * The user name.
99       */
100     @Parameter(property = "username")
101     private String username;
102 
103     /**
104      * The user password.
105      */
106     @Parameter(property = "password")
107     private String password;
108 
109     /**
110      * The private key.
111      */
112     @Parameter(property = "privateKey")
113     private String privateKey;
114 
115     /**
116      * The passphrase.
117      */
118     @Parameter(property = "passphrase")
119     private String passphrase;
120 
121     /**
122      * The url of tags base directory (used by svn protocol). It is not
123      * necessary to set it if you use the standard svn layout
124      * (branches/tags/trunk).
125      */
126     @Parameter(property = "tagBase")
127     private String tagBase;
128 
129     /**
130      * Comma separated list of includes file pattern.
131      */
132     @Parameter(property = "includes")
133     private String includes;
134 
135     /**
136      * Comma separated list of excludes file pattern.
137      */
138     @Parameter(property = "excludes")
139     private String excludes;
140 
141     @Component
142     private ScmManager manager;
143 
144     @Component
145     private SettingsDecrypter settingsDecrypter;
146 
147     /**
148      * The base directory.
149      */
150     @Parameter(property = "basedir", required = true)
151     private File basedir;
152 
153     @Parameter(defaultValue = "${settings}", readonly = true)
154     private Settings settings;
155 
156     /**
157      * List of System properties to pass to the JUnit tests.
158      */
159     @Parameter
160     private Properties systemProperties;
161 
162     /**
163      * List of provider implementations.
164      */
165     @Parameter
166     private Map<String, String> providerImplementations;
167 
168     /**
169      * Should distributed changes be pushed to the central repository?
170      * For many distributed SCMs like Git, a change like a commit
171      * is only stored in your local copy of the repository.  Pushing
172      * the change allows your to more easily share it with other users.
173      *
174      * @since 1.4
175      */
176     @Parameter(property = "pushChanges", defaultValue = "true")
177     private boolean pushChanges;
178 
179     /**
180      * A workItem for SCMs like RTC, TFS etc, that may require additional
181      * information to perform a pushChange operation.
182      *
183      * @since 1.9.5
184      */
185     @Parameter(property = "workItem")
186     @Deprecated
187     private String workItem;
188 
189     /** {@inheritDoc} */
190     public void execute() throws MojoExecutionException {
191         if (systemProperties != null) {
192             // Add all system properties configured by the user
193             Iterator<Object> iter = systemProperties.keySet().iterator();
194 
195             while (iter.hasNext()) {
196                 String key = (String) iter.next();
197 
198                 String value = systemProperties.getProperty(key);
199 
200                 System.setProperty(key, value);
201             }
202         }
203 
204         if (providerImplementations != null && !providerImplementations.isEmpty()) {
205             for (Entry<String, String> entry : providerImplementations.entrySet()) {
206                 String providerType = entry.getKey();
207                 String providerImplementation = entry.getValue();
208                 getLog().info("Change the default '" + providerType + "' provider implementation to '"
209                         + providerImplementation + "'.");
210                 getScmManager().setScmProviderImplementation(providerType, providerImplementation);
211             }
212         }
213     }
214 
215     protected void setConnectionType(String connectionType) {
216         this.connectionType = connectionType;
217     }
218 
219     public String getConnectionUrl() {
220         boolean requireDeveloperConnection = !"connection".equals(connectionType.toLowerCase());
221         if ((connectionUrl != null && !connectionUrl.isEmpty()) && !requireDeveloperConnection) {
222             return connectionUrl;
223         } else if (developerConnectionUrl != null && !developerConnectionUrl.isEmpty()) {
224             return developerConnectionUrl;
225         }
226         if (requireDeveloperConnection) {
227             throw new NullPointerException("You need to define a developerConnectionUrl parameter");
228         } else {
229             throw new NullPointerException("You need to define a connectionUrl parameter");
230         }
231     }
232 
233     public void setConnectionUrl(String connectionUrl) {
234         this.connectionUrl = connectionUrl;
235     }
236 
237     public File getWorkingDirectory() {
238         if (workingDirectory == null) {
239             return basedir;
240         }
241 
242         return workingDirectory;
243     }
244 
245     public File getBasedir() {
246         return this.basedir;
247     }
248 
249     public void setWorkingDirectory(File workingDirectory) {
250         this.workingDirectory = workingDirectory;
251     }
252 
253     public ScmManager getScmManager() {
254         return manager;
255     }
256 
257     public ScmFileSet getFileSet() throws IOException {
258         if (includes != null || excludes != null) {
259             return new ScmFileSet(getWorkingDirectory(), includes, excludes);
260         } else {
261             return new ScmFileSet(getWorkingDirectory());
262         }
263     }
264 
265     public ScmRepository getScmRepository() throws ScmException {
266         ScmRepository repository;
267 
268         try {
269             repository = getScmManager().makeScmRepository(getConnectionUrl());
270 
271             ScmProviderRepository providerRepo = repository.getProviderRepository();
272 
273             providerRepo.setPushChanges(pushChanges);
274 
275             if (!(workItem == null || workItem.isEmpty())) {
276                 providerRepo.setWorkItem(workItem);
277             }
278 
279             if (!(username == null || username.isEmpty())) {
280                 providerRepo.setUser(username);
281             }
282 
283             if (!(password == null || password.isEmpty())) {
284                 providerRepo.setPassword(password);
285             }
286 
287             if (repository.getProviderRepository() instanceof ScmProviderRepositoryWithHost) {
288                 ScmProviderRepositoryWithHost repo = (ScmProviderRepositoryWithHost) repository.getProviderRepository();
289 
290                 loadInfosFromSettings(repo);
291 
292                 if (!(username == null || username.isEmpty())) {
293                     repo.setUser(username);
294                 }
295 
296                 if (!(password == null || password.isEmpty())) {
297                     repo.setPassword(password);
298                 }
299 
300                 if (!(privateKey == null || privateKey.isEmpty())) {
301                     repo.setPrivateKey(privateKey);
302                 }
303 
304                 if (!(passphrase == null || passphrase.isEmpty())) {
305                     repo.setPassphrase(passphrase);
306                 }
307             }
308 
309             if (!(tagBase == null || tagBase.isEmpty())
310                     && repository.getProvider().equals("svn")) {
311                 SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) repository.getProviderRepository();
312 
313                 svnRepo.setTagBase(tagBase);
314             }
315         } catch (ScmRepositoryException e) {
316             if (!e.getValidationMessages().isEmpty()) {
317                 for (String message : e.getValidationMessages()) {
318                     getLog().error(message);
319                 }
320             }
321 
322             throw new ScmException("Can't load the scm provider.", e);
323         } catch (Exception e) {
324             throw new ScmException("Can't load the scm provider.", e);
325         }
326 
327         return repository;
328     }
329 
330     /**
331      * Load username password from settings if user has not set them in JVM properties
332      *
333      * @param repo not null
334      */
335     private void loadInfosFromSettings(ScmProviderRepositoryWithHost repo) {
336         if (username == null || password == null) {
337             String host = repo.getHost();
338 
339             int port = repo.getPort();
340 
341             if (port > 0) {
342                 host += ":" + port;
343             }
344 
345             Server server = this.settings.getServer(host);
346 
347             if (server != null) {
348                 server = decrypt(server);
349 
350                 if (username == null) {
351                     username = server.getUsername();
352                 }
353 
354                 if (password == null) {
355                     password = server.getPassword();
356                 }
357 
358                 if (privateKey == null) {
359                     privateKey = server.getPrivateKey();
360                 }
361 
362                 if (passphrase == null) {
363                     passphrase = server.getPassphrase();
364                 }
365             }
366         }
367     }
368 
369     private Server decrypt(Server server) {
370         SettingsDecryptionResult result = settingsDecrypter.decrypt(new DefaultSettingsDecryptionRequest(server));
371         for (SettingsProblem problem : result.getProblems()) {
372             getLog().error(problem.getMessage(), problem.getException());
373         }
374 
375         return result.getServer();
376     }
377 
378     public void checkResult(ScmResult result) throws MojoExecutionException {
379         if (!result.isSuccess()) {
380             getLog().error("Provider message:");
381 
382             getLog().error(result.getProviderMessage() == null ? "" : result.getProviderMessage());
383 
384             getLog().error("Command output:");
385 
386             getLog().error(result.getCommandOutput() == null ? "" : result.getCommandOutput());
387 
388             throw new MojoExecutionException("Command failed: " + Objects.toString(result.getProviderMessage()));
389         }
390     }
391 
392     public String getIncludes() {
393         return includes;
394     }
395 
396     public void setIncludes(String includes) {
397         this.includes = includes;
398     }
399 
400     public String getExcludes() {
401         return excludes;
402     }
403 
404     public void setExcludes(String excludes) {
405         this.excludes = excludes;
406     }
407 
408     public ScmVersion getScmVersion(String versionType, String version) throws MojoExecutionException {
409         if ((versionType == null || versionType.isEmpty()) && (version != null && !version.isEmpty())) {
410             throw new MojoExecutionException("You must specify the version type.");
411         }
412 
413         if (version == null || version.isEmpty()) {
414             return null;
415         }
416 
417         if (VERSION_TYPE_BRANCH.equals(versionType)) {
418             return new ScmBranch(version);
419         }
420 
421         if (VERSION_TYPE_TAG.equals(versionType)) {
422             return new ScmTag(version);
423         }
424 
425         if (VERSION_TYPE_REVISION.equals(versionType)) {
426             return new ScmRevision(version);
427         }
428 
429         throw new MojoExecutionException("Unknown '" + versionType + "' version type.");
430     }
431 
432     protected void handleExcludesIncludesAfterCheckoutAndExport(File checkoutDirectory) throws MojoExecutionException {
433         List<String> includes = new ArrayList<>();
434 
435         if (!StringUtils.isBlank(this.getIncludes())) {
436             String[] tokens = StringUtils.split(this.getIncludes(), ",");
437             for (int i = 0; i < tokens.length; ++i) {
438                 includes.add(tokens[i]);
439             }
440         }
441 
442         List<String> excludes = new ArrayList<>();
443 
444         if (!StringUtils.isBlank(this.getExcludes())) {
445             String[] tokens = StringUtils.split(this.getExcludes(), ",");
446             for (int i = 0; i < tokens.length; ++i) {
447                 excludes.add(tokens[i]);
448             }
449         }
450 
451         if (includes.isEmpty() && excludes.isEmpty()) {
452             return;
453         }
454 
455         FileSetManager fileSetManager = new FileSetManager();
456 
457         FileSet fileset = new FileSet();
458         fileset.setDirectory(checkoutDirectory.getAbsolutePath());
459         fileset.setIncludes(excludes); // revert the order to do the delete
460         fileset.setExcludes(includes);
461         fileset.setUseDefaultExcludes(false);
462 
463         try {
464             fileSetManager.delete(fileset);
465         } catch (IOException e) {
466             throw new MojoExecutionException(
467                     "Error found while cleaning up output directory base on " + "excludes/includes configurations.", e);
468         }
469     }
470 }