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.maven.plugin.AbstractMojo;
32  import org.apache.maven.plugin.MojoExecutionException;
33  import org.apache.maven.plugins.annotations.Component;
34  import org.apache.maven.plugins.annotations.Parameter;
35  import org.apache.maven.scm.ScmBranch;
36  import org.apache.maven.scm.ScmException;
37  import org.apache.maven.scm.ScmFileSet;
38  import org.apache.maven.scm.ScmResult;
39  import org.apache.maven.scm.ScmRevision;
40  import org.apache.maven.scm.ScmTag;
41  import org.apache.maven.scm.ScmVersion;
42  import org.apache.maven.scm.manager.ScmManager;
43  import org.apache.maven.scm.provider.ScmProviderRepository;
44  import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
45  import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
46  import org.apache.maven.scm.repository.ScmRepository;
47  import org.apache.maven.scm.repository.ScmRepositoryException;
48  import org.apache.maven.settings.Server;
49  import org.apache.maven.settings.Settings;
50  import org.apache.maven.settings.building.SettingsProblem;
51  import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
52  import org.apache.maven.settings.crypto.SettingsDecrypter;
53  import org.apache.maven.settings.crypto.SettingsDecryptionResult;
54  import org.apache.maven.shared.model.fileset.FileSet;
55  import org.apache.maven.shared.model.fileset.util.FileSetManager;
56  import org.codehaus.plexus.util.StringUtils;
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 (StringUtils.isNotEmpty(connectionUrl) && !requireDeveloperConnection) {
222             return connectionUrl;
223         } else if (StringUtils.isNotEmpty(developerConnectionUrl)) {
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 (!StringUtils.isEmpty(workItem)) {
276                 providerRepo.setWorkItem(workItem);
277             }
278 
279             if (!StringUtils.isEmpty(username)) {
280                 providerRepo.setUser(username);
281             }
282 
283             if (!StringUtils.isEmpty(password)) {
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 (!StringUtils.isEmpty(username)) {
293                     repo.setUser(username);
294                 }
295 
296                 if (!StringUtils.isEmpty(password)) {
297                     repo.setPassword(password);
298                 }
299 
300                 if (!StringUtils.isEmpty(privateKey)) {
301                     repo.setPrivateKey(privateKey);
302                 }
303 
304                 if (!StringUtils.isEmpty(passphrase)) {
305                     repo.setPassphrase(passphrase);
306                 }
307             }
308 
309             if (!StringUtils.isEmpty(tagBase) && repository.getProvider().equals("svn")) {
310                 SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) repository.getProviderRepository();
311 
312                 svnRepo.setTagBase(tagBase);
313             }
314         } catch (ScmRepositoryException e) {
315             if (!e.getValidationMessages().isEmpty()) {
316                 for (String message : e.getValidationMessages()) {
317                     getLog().error(message);
318                 }
319             }
320 
321             throw new ScmException("Can't load the scm provider.", e);
322         } catch (Exception e) {
323             throw new ScmException("Can't load the scm provider.", e);
324         }
325 
326         return repository;
327     }
328 
329     /**
330      * Load username password from settings if user has not set them in JVM properties
331      *
332      * @param repo not null
333      */
334     private void loadInfosFromSettings(ScmProviderRepositoryWithHost repo) {
335         if (username == null || password == null) {
336             String host = repo.getHost();
337 
338             int port = repo.getPort();
339 
340             if (port > 0) {
341                 host += ":" + port;
342             }
343 
344             Server server = this.settings.getServer(host);
345 
346             if (server != null) {
347                 server = decrypt(server);
348 
349                 if (username == null) {
350                     username = server.getUsername();
351                 }
352 
353                 if (password == null) {
354                     password = server.getPassword();
355                 }
356 
357                 if (privateKey == null) {
358                     privateKey = server.getPrivateKey();
359                 }
360 
361                 if (passphrase == null) {
362                     passphrase = server.getPassphrase();
363                 }
364             }
365         }
366     }
367 
368     private Server decrypt(Server server) {
369         SettingsDecryptionResult result = settingsDecrypter.decrypt(new DefaultSettingsDecryptionRequest(server));
370         for (SettingsProblem problem : result.getProblems()) {
371             getLog().error(problem.getMessage(), problem.getException());
372         }
373 
374         return result.getServer();
375     }
376 
377     public void checkResult(ScmResult result) throws MojoExecutionException {
378         if (!result.isSuccess()) {
379             getLog().error("Provider message:");
380 
381             getLog().error(result.getProviderMessage() == null ? "" : result.getProviderMessage());
382 
383             getLog().error("Command output:");
384 
385             getLog().error(result.getCommandOutput() == null ? "" : result.getCommandOutput());
386 
387             throw new MojoExecutionException("Command failed: " + Objects.toString(result.getProviderMessage()));
388         }
389     }
390 
391     public String getIncludes() {
392         return includes;
393     }
394 
395     public void setIncludes(String includes) {
396         this.includes = includes;
397     }
398 
399     public String getExcludes() {
400         return excludes;
401     }
402 
403     public void setExcludes(String excludes) {
404         this.excludes = excludes;
405     }
406 
407     public ScmVersion getScmVersion(String versionType, String version) throws MojoExecutionException {
408         if (StringUtils.isEmpty(versionType) && StringUtils.isNotEmpty(version)) {
409             throw new MojoExecutionException("You must specify the version type.");
410         }
411 
412         if (StringUtils.isEmpty(version)) {
413             return null;
414         }
415 
416         if (VERSION_TYPE_BRANCH.equals(versionType)) {
417             return new ScmBranch(version);
418         }
419 
420         if (VERSION_TYPE_TAG.equals(versionType)) {
421             return new ScmTag(version);
422         }
423 
424         if (VERSION_TYPE_REVISION.equals(versionType)) {
425             return new ScmRevision(version);
426         }
427 
428         throw new MojoExecutionException("Unknown '" + versionType + "' version type.");
429     }
430 
431     protected void handleExcludesIncludesAfterCheckoutAndExport(File checkoutDirectory) throws MojoExecutionException {
432         List<String> includes = new ArrayList<String>();
433 
434         if (!StringUtils.isBlank(this.getIncludes())) {
435             String[] tokens = StringUtils.split(this.getIncludes(), ",");
436             for (int i = 0; i < tokens.length; ++i) {
437                 includes.add(tokens[i]);
438             }
439         }
440 
441         List<String> excludes = new ArrayList<String>();
442 
443         if (!StringUtils.isBlank(this.getExcludes())) {
444             String[] tokens = StringUtils.split(this.getExcludes(), ",");
445             for (int i = 0; i < tokens.length; ++i) {
446                 excludes.add(tokens[i]);
447             }
448         }
449 
450         if (includes.isEmpty() && excludes.isEmpty()) {
451             return;
452         }
453 
454         FileSetManager fileSetManager = new FileSetManager();
455 
456         FileSet fileset = new FileSet();
457         fileset.setDirectory(checkoutDirectory.getAbsolutePath());
458         fileset.setIncludes(excludes); // revert the order to do the delete
459         fileset.setExcludes(includes);
460         fileset.setUseDefaultExcludes(false);
461 
462         try {
463             fileSetManager.delete(fileset);
464         } catch (IOException e) {
465             throw new MojoExecutionException(
466                     "Error found while cleaning up output directory base on " + "excludes/includes configurations.", e);
467         }
468     }
469 }