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.provider.svn;
20  
21  import java.io.File;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.apache.commons.lang3.StringUtils;
26  import org.apache.maven.scm.CommandParameters;
27  import org.apache.maven.scm.ScmException;
28  import org.apache.maven.scm.ScmFileSet;
29  import org.apache.maven.scm.ScmResult;
30  import org.apache.maven.scm.command.add.AddScmResult;
31  import org.apache.maven.scm.command.blame.BlameScmResult;
32  import org.apache.maven.scm.command.branch.BranchScmResult;
33  import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
34  import org.apache.maven.scm.command.checkin.CheckInScmResult;
35  import org.apache.maven.scm.command.checkout.CheckOutScmResult;
36  import org.apache.maven.scm.command.diff.DiffScmResult;
37  import org.apache.maven.scm.command.export.ExportScmResult;
38  import org.apache.maven.scm.command.info.InfoItem;
39  import org.apache.maven.scm.command.info.InfoScmResult;
40  import org.apache.maven.scm.command.list.ListScmResult;
41  import org.apache.maven.scm.command.mkdir.MkdirScmResult;
42  import org.apache.maven.scm.command.remove.RemoveScmResult;
43  import org.apache.maven.scm.command.status.StatusScmResult;
44  import org.apache.maven.scm.command.tag.TagScmResult;
45  import org.apache.maven.scm.command.untag.UntagScmResult;
46  import org.apache.maven.scm.command.update.UpdateScmResult;
47  import org.apache.maven.scm.provider.AbstractScmProvider;
48  import org.apache.maven.scm.provider.ScmProviderRepository;
49  import org.apache.maven.scm.provider.svn.command.SvnCommand;
50  import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
51  import org.apache.maven.scm.provider.svn.util.SvnUtil;
52  import org.apache.maven.scm.repository.ScmRepository;
53  import org.apache.maven.scm.repository.ScmRepositoryException;
54  import org.apache.maven.scm.repository.UnknownRepositoryStructure;
55  
56  /**
57   * SCM Provider for Subversion
58   *
59   * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
60   *
61   */
62  public abstract class AbstractSvnScmProvider extends AbstractScmProvider {
63      // ----------------------------------------------------------------------
64      //
65      // ----------------------------------------------------------------------
66  
67      private static class ScmUrlParserResult {
68          private final List<String> messages = new ArrayList<>();
69  
70          private ScmProviderRepository repository;
71      }
72  
73      public static final String CURRENT_WORKING_DIRECTORY = "scmCheckWorkingDirectoryUrl.currentWorkingDirectory";
74  
75      // ----------------------------------------------------------------------
76      // ScmProvider Implementation
77      // ----------------------------------------------------------------------
78  
79      /**
80       * {@inheritDoc}
81       */
82      public String getScmSpecificFilename() {
83          return ".svn";
84      }
85  
86      /**
87       * {@inheritDoc}
88       */
89      public ScmProviderRepository makeProviderScmRepository(String scmSpecificUrl, char delimiter)
90              throws ScmRepositoryException {
91          ScmUrlParserResult result = parseScmUrl(scmSpecificUrl);
92  
93          if (checkCurrentWorkingDirectoryUrl()) {
94              logger.debug("Checking svn info 'URL:' field matches current sources directory");
95              try {
96                  String workingDir = System.getProperty(CURRENT_WORKING_DIRECTORY);
97                  InfoScmResult info =
98                          info(result.repository, new ScmFileSet(new File(workingDir)), new CommandParameters());
99  
100                 String url = findUrlInfoItem(info);
101                 String comparison = "'" + url + "' vs. '" + scmSpecificUrl + "'";
102                 logger.debug("Comparing : " + comparison);
103                 if (url != null && !url.equals(scmSpecificUrl)) {
104                     result.messages.add("Scm url does not match the value returned by svn info (" + comparison + ")");
105                 }
106             } catch (ScmException e) {
107                 throw new ScmRepositoryException("An error occurred while trying to svn info", e);
108             }
109         }
110         if (result.messages.size() > 0) {
111             throw new ScmRepositoryException("The scm url is invalid.", result.messages);
112         }
113 
114         return result.repository;
115     }
116 
117     private boolean checkCurrentWorkingDirectoryUrl() {
118         return StringUtils.isNotEmpty(System.getProperty(CURRENT_WORKING_DIRECTORY));
119     }
120 
121     private String findUrlInfoItem(InfoScmResult infoScmResult) {
122         for (InfoItem infoItem : infoScmResult.getInfoItems()) {
123             if (infoItem.getURL() != null) {
124                 logger.debug("URL found: " + infoItem.getURL());
125                 return infoItem.getURL();
126             }
127         }
128         logger.debug("URL not found (command output=" + infoScmResult.getCommandOutput() + ")");
129         return null;
130     }
131 
132     /**
133      * {@inheritDoc}
134      */
135     public ScmProviderRepository makeProviderScmRepository(File path)
136             throws ScmRepositoryException, UnknownRepositoryStructure {
137         if (path == null) {
138             throw new NullPointerException("Path argument is null");
139         }
140 
141         if (!path.isDirectory()) {
142             throw new ScmRepositoryException(path.getAbsolutePath() + " isn't a valid directory.");
143         }
144 
145         if (!new File(path, ".svn").exists()) {
146             throw new ScmRepositoryException(path.getAbsolutePath() + " isn't a svn checkout directory.");
147         }
148 
149         try {
150             return makeProviderScmRepository(getRepositoryURL(path), ':');
151         } catch (ScmException e) {
152             // XXX We should allow throwing of SCMException.
153             throw new ScmRepositoryException("Error executing info command", e);
154         }
155     }
156 
157     protected abstract String getRepositoryURL(File path) throws ScmException;
158 
159     /**
160      * {@inheritDoc}
161      */
162     public List<String> validateScmUrl(String scmSpecificUrl, char delimiter) {
163         List<String> messages = new ArrayList<>();
164         try {
165             makeProviderScmRepository(scmSpecificUrl, delimiter);
166         } catch (ScmRepositoryException e) {
167             messages = e.getValidationMessages();
168         }
169         return messages;
170     }
171 
172     /**
173      * {@inheritDoc}
174      */
175     public String getScmType() {
176         return "svn";
177     }
178 
179     // ----------------------------------------------------------------------
180     //
181     // ----------------------------------------------------------------------
182 
183     private ScmUrlParserResult parseScmUrl(String scmSpecificUrl) {
184         ScmUrlParserResult result = new ScmUrlParserResult();
185 
186         // ----------------------------------------------------------------------
187         // Do some sanity checking of the SVN url
188         // ----------------------------------------------------------------------
189 
190         if (scmSpecificUrl.startsWith("file")) {
191             if (!scmSpecificUrl.startsWith("file://")) {
192                 result.messages.add("A svn 'file' url must be on the form 'file://[hostname]/'.");
193 
194                 return result;
195             }
196         } else if (scmSpecificUrl.startsWith("https")) {
197             if (!scmSpecificUrl.startsWith("https://")) {
198                 result.messages.add("A svn 'http' url must be on the form 'https://'.");
199 
200                 return result;
201             }
202         } else if (scmSpecificUrl.startsWith("http")) {
203             if (!scmSpecificUrl.startsWith("http://")) {
204                 result.messages.add("A svn 'http' url must be on the form 'http://'.");
205 
206                 return result;
207             }
208         }
209         // Support of tunnels: svn+xxx with xxx defined in subversion conf file
210         else if (scmSpecificUrl.startsWith("svn+")) {
211             if (scmSpecificUrl.indexOf("://") < 0) {
212                 result.messages.add("A svn 'svn+xxx' url must be on the form 'svn+xxx://'.");
213 
214                 return result;
215             } else {
216                 String tunnel = scmSpecificUrl.substring("svn+".length(), scmSpecificUrl.indexOf("://"));
217 
218                 // ssh is always an allowed tunnel
219                 if (!"ssh".equals(tunnel)) {
220                     SvnConfigFileReader reader = new SvnConfigFileReader();
221                     if (SvnUtil.getSettings().getConfigDirectory() != null) {
222                         reader.setConfigDirectory(new File(SvnUtil.getSettings().getConfigDirectory()));
223                     }
224 
225                     if (StringUtils.isEmpty(reader.getProperty("tunnels", tunnel))) {
226                         result.messages.add(
227                                 "The tunnel '" + tunnel + "' isn't defined in your subversion configuration file.");
228 
229                         return result;
230                     }
231                 }
232             }
233         } else if (scmSpecificUrl.startsWith("svn")) {
234             if (!scmSpecificUrl.startsWith("svn://")) {
235                 result.messages.add("A svn 'svn' url must be on the form 'svn://'.");
236 
237                 return result;
238             }
239         } else {
240             result.messages.add(scmSpecificUrl + " url isn't a valid svn URL.");
241 
242             return result;
243         }
244 
245         result.repository = new SvnScmProviderRepository(scmSpecificUrl);
246 
247         return result;
248     }
249 
250     protected abstract SvnCommand getAddCommand();
251 
252     /**
253      * {@inheritDoc}
254      */
255     public AddScmResult add(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
256             throws ScmException {
257         return (AddScmResult) executeCommand(getAddCommand(), repository, fileSet, parameters);
258     }
259 
260     protected abstract SvnCommand getBranchCommand();
261 
262     /**
263      * {@inheritDoc}
264      */
265     protected BranchScmResult branch(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
266             throws ScmException {
267         return (BranchScmResult) executeCommand(getBranchCommand(), repository, fileSet, parameters);
268     }
269 
270     protected abstract SvnCommand getChangeLogCommand();
271 
272     /**
273      * {@inheritDoc}
274      */
275     public ChangeLogScmResult changelog(
276             ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters) throws ScmException {
277         return (ChangeLogScmResult) executeCommand(getChangeLogCommand(), repository, fileSet, parameters);
278     }
279 
280     protected abstract SvnCommand getCheckInCommand();
281 
282     /**
283      * {@inheritDoc}
284      */
285     public CheckInScmResult checkin(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
286             throws ScmException {
287         return (CheckInScmResult) executeCommand(getCheckInCommand(), repository, fileSet, parameters);
288     }
289 
290     protected abstract SvnCommand getCheckOutCommand();
291 
292     /**
293      * {@inheritDoc}
294      */
295     public CheckOutScmResult checkout(
296             ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters) throws ScmException {
297         return (CheckOutScmResult) executeCommand(getCheckOutCommand(), repository, fileSet, parameters);
298     }
299 
300     protected abstract SvnCommand getDiffCommand();
301 
302     /**
303      * {@inheritDoc}
304      */
305     public DiffScmResult diff(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
306             throws ScmException {
307         return (DiffScmResult) executeCommand(getDiffCommand(), repository, fileSet, parameters);
308     }
309 
310     protected abstract SvnCommand getExportCommand();
311 
312     /**
313      * {@inheritDoc}
314      */
315     protected ExportScmResult export(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
316             throws ScmException {
317         return (ExportScmResult) executeCommand(getExportCommand(), repository, fileSet, parameters);
318     }
319 
320     protected abstract SvnCommand getRemoveCommand();
321 
322     /**
323      * {@inheritDoc}
324      */
325     public RemoveScmResult remove(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
326             throws ScmException {
327         return (RemoveScmResult) executeCommand(getRemoveCommand(), repository, fileSet, parameters);
328     }
329 
330     protected abstract SvnCommand getStatusCommand();
331 
332     /**
333      * {@inheritDoc}
334      */
335     public StatusScmResult status(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
336             throws ScmException {
337         return (StatusScmResult) executeCommand(getStatusCommand(), repository, fileSet, parameters);
338     }
339 
340     protected abstract SvnCommand getTagCommand();
341 
342     /**
343      * {@inheritDoc}
344      */
345     public TagScmResult tag(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
346             throws ScmException {
347         return (TagScmResult) executeCommand(getTagCommand(), repository, fileSet, parameters);
348     }
349 
350     protected abstract SvnCommand getUntagCommand();
351 
352     /**
353      * {@inheritDoc}
354      */
355     @Override
356     public UntagScmResult untag(ScmRepository repository, ScmFileSet fileSet, CommandParameters parameters)
357             throws ScmException {
358         return (UntagScmResult)
359                 executeCommand(getUntagCommand(), repository.getProviderRepository(), fileSet, parameters);
360     }
361 
362     protected abstract SvnCommand getUpdateCommand();
363 
364     /**
365      * {@inheritDoc}
366      */
367     public UpdateScmResult update(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
368             throws ScmException {
369         return (UpdateScmResult) executeCommand(getUpdateCommand(), repository, fileSet, parameters);
370     }
371 
372     protected ScmResult executeCommand(
373             SvnCommand command, ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
374             throws ScmException {
375         return command.execute(repository, fileSet, parameters);
376     }
377 
378     protected abstract SvnCommand getListCommand();
379 
380     /**
381      * {@inheritDoc}
382      */
383     public ListScmResult list(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
384             throws ScmException {
385         SvnCommand cmd = getListCommand();
386 
387         return (ListScmResult) executeCommand(cmd, repository, fileSet, parameters);
388     }
389 
390     protected abstract SvnCommand getInfoCommand();
391 
392     public InfoScmResult info(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
393             throws ScmException {
394         SvnCommand cmd = getInfoCommand();
395 
396         return (InfoScmResult) executeCommand(cmd, repository, fileSet, parameters);
397     }
398 
399     /**
400      * {@inheritDoc}
401      */
402     protected BlameScmResult blame(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
403             throws ScmException {
404         SvnCommand cmd = getBlameCommand();
405 
406         return (BlameScmResult) executeCommand(cmd, repository, fileSet, parameters);
407     }
408 
409     protected abstract SvnCommand getBlameCommand();
410 
411     /**
412      * {@inheritDoc}
413      */
414     public MkdirScmResult mkdir(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
415             throws ScmException {
416         SvnCommand cmd = getMkdirCommand();
417 
418         return (MkdirScmResult) executeCommand(cmd, repository, fileSet, parameters);
419     }
420 
421     protected abstract SvnCommand getMkdirCommand();
422 
423     /**
424      * @param repository
425      * @param parameters
426      * @return true if remote url exists
427      * @throws ScmException
428      * @since 1.8
429      */
430     public abstract boolean remoteUrlExist(ScmProviderRepository repository, CommandParameters parameters)
431             throws ScmException;
432 }