View Javadoc
1   package org.apache.maven.scm.provider.accurev;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *    http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.Date;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import org.apache.maven.scm.ChangeFile;
32  import org.apache.maven.scm.ChangeSet;
33  import org.apache.maven.scm.CommandParameter;
34  import org.apache.maven.scm.CommandParameters;
35  import org.apache.maven.scm.ScmException;
36  import org.apache.maven.scm.ScmFile;
37  import org.apache.maven.scm.ScmFileSet;
38  import org.apache.maven.scm.ScmRevision;
39  import org.apache.maven.scm.command.add.AddScmResult;
40  import org.apache.maven.scm.command.blame.BlameScmResult;
41  import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
42  import org.apache.maven.scm.command.checkin.CheckInScmResult;
43  import org.apache.maven.scm.command.checkout.CheckOutScmResult;
44  import org.apache.maven.scm.command.export.ExportScmResult;
45  import org.apache.maven.scm.command.login.LoginScmResult;
46  import org.apache.maven.scm.command.remove.RemoveScmResult;
47  import org.apache.maven.scm.command.status.StatusScmResult;
48  import org.apache.maven.scm.command.tag.TagScmResult;
49  import org.apache.maven.scm.command.update.UpdateScmResult;
50  import org.apache.maven.scm.provider.AbstractScmProvider;
51  import org.apache.maven.scm.provider.ScmProviderRepository;
52  import org.apache.maven.scm.provider.accurev.cli.AccuRevCommandLine;
53  import org.apache.maven.scm.provider.accurev.command.add.AccuRevAddCommand;
54  import org.apache.maven.scm.provider.accurev.command.blame.AccuRevBlameCommand;
55  import org.apache.maven.scm.provider.accurev.command.changelog.AccuRevChangeLogCommand;
56  import org.apache.maven.scm.provider.accurev.command.checkin.AccuRevCheckInCommand;
57  import org.apache.maven.scm.provider.accurev.command.checkout.AccuRevCheckOutCommand;
58  import org.apache.maven.scm.provider.accurev.command.export.AccuRevExportCommand;
59  import org.apache.maven.scm.provider.accurev.command.login.AccuRevLoginCommand;
60  import org.apache.maven.scm.provider.accurev.command.remove.AccuRevRemoveCommand;
61  import org.apache.maven.scm.provider.accurev.command.status.AccuRevStatusCommand;
62  import org.apache.maven.scm.provider.accurev.command.tag.AccuRevTagCommand;
63  import org.apache.maven.scm.provider.accurev.command.update.AccuRevUpdateCommand;
64  import org.apache.maven.scm.provider.accurev.command.update.AccuRevUpdateScmResult;
65  import org.apache.maven.scm.provider.accurev.util.QuotedPropertyParser;
66  import org.apache.maven.scm.repository.ScmRepositoryException;
67  import org.apache.maven.scm.repository.UnknownRepositoryStructure;
68  import org.codehaus.plexus.util.StringUtils;
69  
70  /**
71   * AccuRev integration with Maven SCM
72   * 
73   * @plexus.component role="org.apache.maven.scm.provider.ScmProvider" role-hint="accurev"
74   */
75  public class AccuRevScmProvider
76      extends AbstractScmProvider
77  {
78  
79      public static final String ACCUREV_EXECUTABLE_PROPERTY = "accurevExe";
80  
81      public static final String TAG_FORMAT_PROPERTY = "tagFormat";
82  
83      public static final String SYSTEM_PROPERTY_PREFIX = "maven.scm.accurev.";
84  
85      public String getScmType()
86      {
87  
88          return "accurev";
89      }
90  
91      /**
92       * The basic url parsing approach is to be as loose as possible. If you specify as per the docs you'll get what you
93       * expect. If you do something else the result is undefined. Don't use "/" "\" or "@" as the delimiter,
94       */
95      public ScmProviderRepository makeProviderScmRepository( String scmSpecificUrl, char delimiter )
96          throws ScmRepositoryException
97      {
98  
99          List<String> validationMessages = new ArrayList<String>();
100 
101         String[] tokens = StringUtils.split( scmSpecificUrl, Character.toString( delimiter ) );
102 
103         // [[user][/pass]@host[:port]][:stream][:\project\dir]
104 
105         String basisStream = null;
106         String projectPath = null;
107         int port = AccuRev.DEFAULT_PORT;
108         String host = null;
109         String user = null;
110         String password = null;
111         Map<String, String> properties = new HashMap<String, String>();
112         properties.put( TAG_FORMAT_PROPERTY, AccuRevScmProviderRepository.DEFAULT_TAG_FORMAT );
113         properties.put( ACCUREV_EXECUTABLE_PROPERTY, AccuRev.DEFAULT_ACCUREV_EXECUTABLE );
114 
115         fillSystemProperties( properties );
116 
117         int i = 0;
118         while ( i < tokens.length )
119         {
120             int at = tokens[i].indexOf( '@' );
121             // prefer "/", better not have a "/" or a "\\" in your password.
122             int slash = tokens[i].indexOf( '/' );
123             slash = slash < 0 ? tokens[i].indexOf( '\\' ) : slash;
124 
125             int qMark = tokens[i].indexOf( '?' );
126 
127             if ( qMark == 0 )
128             {
129                 QuotedPropertyParser.parse( tokens[i].substring( 1 ), properties );
130             }
131             else if ( slash == 0 )
132             {
133                 // this is the project path
134                 projectPath = tokens[i].substring( 1 );
135                 break;
136             }
137             else if ( ( slash > 0 || ( at >= 0 ) ) && host == null && user == null )
138             {
139                 // user/pass@host
140                 int len = tokens[i].length();
141                 if ( at >= 0 && len > at )
142                 {
143                     // everything after the "@"
144                     host = tokens[i].substring( at + 1 );
145                 }
146 
147                 if ( slash > 0 )
148                 {
149                     // user up to /
150                     user = tokens[i].substring( 0, slash );
151                     // pass between / and @
152                     password = tokens[i].substring( slash + 1, at < 0 ? len : at );
153                 }
154                 else
155                 {
156                     // no /, user from beginning to @
157                     user = tokens[i].substring( 0, at < 0 ? len : at );
158                 }
159 
160             }
161             else if ( host != null && tokens[i].matches( "^[0-9]+$" ) )
162             {
163                 // only valid entry with all digits is the port specification.
164                 port = Integer.parseInt( tokens[i] );
165             }
166             else
167             {
168                 basisStream = tokens[i];
169             }
170 
171             i++;
172         }
173 
174         if ( i < tokens.length )
175         {
176             validationMessages.add( "Unknown tokens in URL " + scmSpecificUrl );
177         }
178 
179         AccuRevScmProviderRepository repo = new AccuRevScmProviderRepository();
180         repo.setLogger( getLogger() );
181         if ( !StringUtils.isEmpty( user ) )
182         {
183             repo.setUser( user );
184         }
185         if ( !StringUtils.isEmpty( password ) )
186         {
187             repo.setPassword( password );
188         }
189         if ( !StringUtils.isEmpty( basisStream ) )
190         {
191             repo.setStreamName( basisStream );
192         }
193         if ( !StringUtils.isEmpty( projectPath ) )
194         {
195             repo.setProjectPath( projectPath );
196         }
197         if ( !StringUtils.isEmpty( host ) )
198         {
199             repo.setHost( host );
200         }
201         repo.setPort( port );
202         repo.setTagFormat( properties.get( TAG_FORMAT_PROPERTY ) );
203 
204         AccuRevCommandLine accuRev = new AccuRevCommandLine( host, port );
205         accuRev.setLogger( getLogger() );
206         accuRev.setExecutable( properties.get( ACCUREV_EXECUTABLE_PROPERTY ) );
207         repo.setAccuRev( accuRev );
208 
209         return repo;
210 
211     }
212 
213     private void fillSystemProperties( Map<String, String> properties )
214     {
215 
216         Set<String> propertyKeys = properties.keySet();
217         for ( String key : propertyKeys )
218         {
219             String systemPropertyKey = SYSTEM_PROPERTY_PREFIX + key;
220             String systemProperty = System.getProperty( systemPropertyKey );
221             if ( systemProperty != null )
222             {
223                 properties.put( key, systemProperty );
224             }
225         }
226 
227     }
228 
229     @Override
230     protected LoginScmResult login( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
231         throws ScmException
232     {
233 
234         if ( getLogger().isDebugEnabled() )
235         {
236             getLogger().debug( repository.toString() );
237         }
238 
239         AccuRevLoginCommand command = new AccuRevLoginCommand( getLogger() );
240         return command.login( repository, fileSet, parameters );
241     }
242 
243     @Override
244     protected CheckOutScmResult checkout( ScmProviderRepository repository, ScmFileSet fileSet,
245                                           CommandParameters parameters )
246         throws ScmException
247     {
248 
249         // workaround deprecated behaviour
250         // TODO pull up to AbstractScmProvider
251         AccuRevScmProviderRepository accuRevRepo = (AccuRevScmProviderRepository) repository;
252         if ( !repository.isPersistCheckout() && accuRevRepo.shouldUseExportForNonPersistentCheckout() )
253         {
254 
255             ExportScmResult result = export( repository, fileSet, parameters );
256             if ( result.isSuccess() )
257             {
258                 return new CheckOutScmResult( result.getCommandLine(), result.getExportedFiles(),
259                                               accuRevRepo.getExportRelativePath() );
260             }
261             else
262             {
263                 return new CheckOutScmResult( result.getCommandLine(), result.getProviderMessage(),
264                                               result.getCommandOutput(), false );
265             }
266         }
267 
268         AccuRevCheckOutCommand command = new AccuRevCheckOutCommand( getLogger() );
269 
270         return command.checkout( repository, fileSet, parameters );
271 
272     }
273 
274     @Override
275     protected CheckInScmResult checkin( ScmProviderRepository repository, ScmFileSet fileSet,
276                                         CommandParameters parameters )
277         throws ScmException
278     {
279 
280         AccuRevCheckInCommand command = new AccuRevCheckInCommand( getLogger() );
281 
282         return command.checkIn( repository, fileSet, parameters );
283     }
284 
285     @Override
286     public ScmProviderRepository makeProviderScmRepository( File path )
287         throws ScmRepositoryException, UnknownRepositoryStructure
288     {
289 
290         // TODO: accurev info with current dir = "path", find workspace. Find use-case for this.
291         return super.makeProviderScmRepository( path );
292     }
293 
294     @Override
295     public AddScmResult add( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
296         throws ScmException
297     {
298         AccuRevAddCommand command = new AccuRevAddCommand( getLogger() );
299         return command.add( repository, fileSet, parameters );
300     }
301 
302     @Override
303     protected TagScmResult tag( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
304         throws ScmException
305     {
306 
307         AccuRevTagCommand command = new AccuRevTagCommand( getLogger() );
308         return command.tag( repository, fileSet, parameters );
309 
310     }
311 
312     @Override
313     protected StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet,
314                                       CommandParameters parameters )
315         throws ScmException
316     {
317 
318         AccuRevStatusCommand command = new AccuRevStatusCommand( getLogger() );
319         return command.status( repository, fileSet, parameters );
320 
321     }
322 
323     @Override
324     protected UpdateScmResult update( ScmProviderRepository repository, ScmFileSet fileSet,
325                                       CommandParameters parameters )
326         throws ScmException
327     {
328 
329         AccuRevScmProviderRepository accurevRepo = (AccuRevScmProviderRepository) repository;
330 
331         AccuRevUpdateCommand command = new AccuRevUpdateCommand( getLogger() );
332 
333         UpdateScmResult result = command.update( repository, fileSet, parameters );
334 
335         if ( result.isSuccess() && parameters.getBoolean( CommandParameter.RUN_CHANGELOG_WITH_UPDATE ) )
336         {
337             AccuRevUpdateScmResult accuRevResult = (AccuRevUpdateScmResult) result;
338 
339             ScmRevision fromRevision = new ScmRevision( accuRevResult.getFromRevision() );
340             ScmRevision toRevision = new ScmRevision( accuRevResult.getToRevision() );
341 
342             parameters.setScmVersion( CommandParameter.START_SCM_VERSION, fromRevision );
343             parameters.setScmVersion( CommandParameter.END_SCM_VERSION, toRevision );
344 
345             AccuRevVersion startVersion = accurevRepo.getAccuRevVersion( fromRevision );
346             AccuRevVersion endVersion = accurevRepo.getAccuRevVersion( toRevision );
347             if ( startVersion.getBasisStream().equals( endVersion.getBasisStream() ) )
348             {
349                 ChangeLogScmResult changeLogResult = changelog( repository, fileSet, parameters );
350 
351                 if ( changeLogResult.isSuccess() )
352                 {
353                     result.setChanges( changeLogResult.getChangeLog().getChangeSets() );
354                 }
355                 else
356                 {
357                     getLogger().warn( "Changelog from " + fromRevision + " to " + toRevision + " failed" );
358                 }
359             }
360             else
361             {
362                 String comment = "Cross stream update result from " + startVersion + " to " + endVersion;
363                 String author = "";
364                 List<ScmFile> files = result.getUpdatedFiles();
365                 List<ChangeFile> changeFiles = new ArrayList<ChangeFile>( files.size() );
366                 for ( ScmFile scmFile : files )
367                 {
368                     changeFiles.add( new ChangeFile( scmFile.getPath() ) );
369                 }
370                 ChangeSet dummyChangeSet = new ChangeSet( new Date(), comment, author, changeFiles );
371                 // different streams invalidates the change log, insert a dummy change instead.
372                 List<ChangeSet> changeSets = Collections.singletonList( dummyChangeSet );
373                 result.setChanges( changeSets );
374             }
375 
376         }
377         return result;
378     }
379 
380     @Override
381     protected ExportScmResult export( ScmProviderRepository repository, ScmFileSet fileSet,
382                                       CommandParameters parameters )
383         throws ScmException
384     {
385 
386         AccuRevExportCommand command = new AccuRevExportCommand( getLogger() );
387         return command.export( repository, fileSet, parameters );
388     }
389 
390     @Override
391     protected ChangeLogScmResult changelog( ScmProviderRepository repository, ScmFileSet fileSet,
392                                             CommandParameters parameters )
393         throws ScmException
394     {
395 
396         AccuRevChangeLogCommand command = new AccuRevChangeLogCommand( getLogger() );
397         return command.changelog( repository, fileSet, parameters );
398     }
399 
400     @Override
401     protected RemoveScmResult remove( ScmProviderRepository repository, ScmFileSet fileSet,
402                                       CommandParameters parameters )
403         throws ScmException
404     {
405 
406         AccuRevRemoveCommand command = new AccuRevRemoveCommand( getLogger() );
407         return command.remove( repository, fileSet, parameters );
408     }
409 
410     /** {@inheritDoc} */
411     @Override
412     protected BlameScmResult blame( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters )
413         throws ScmException
414     {
415 
416         AccuRevBlameCommand blameCommand = new AccuRevBlameCommand( getLogger() );
417         return blameCommand.blame( repository, fileSet, parameters );
418     }
419 }