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