View Javadoc
1   package org.apache.maven.scm.provider.accurev.cli;
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.ByteArrayInputStream;
23  import java.io.File;
24  import java.io.InputStream;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.regex.Pattern;
32  
33  import org.apache.maven.scm.command.blame.BlameLine;
34  import org.apache.maven.scm.log.ScmLogger;
35  import org.apache.maven.scm.provider.accurev.AccuRev;
36  import org.apache.maven.scm.provider.accurev.AccuRevException;
37  import org.apache.maven.scm.provider.accurev.AccuRevInfo;
38  import org.apache.maven.scm.provider.accurev.AccuRevStat;
39  import org.apache.maven.scm.provider.accurev.AccuRevVersion;
40  import org.apache.maven.scm.provider.accurev.CategorisedElements;
41  import org.apache.maven.scm.provider.accurev.FileDifference;
42  import org.apache.maven.scm.provider.accurev.Stream;
43  import org.apache.maven.scm.provider.accurev.Transaction;
44  import org.apache.maven.scm.provider.accurev.WorkSpace;
45  import org.codehaus.plexus.util.Os;
46  import org.codehaus.plexus.util.StringUtils;
47  import org.codehaus.plexus.util.cli.CommandLineException;
48  import org.codehaus.plexus.util.cli.CommandLineUtils;
49  import org.codehaus.plexus.util.cli.Commandline;
50  import org.codehaus.plexus.util.cli.StreamConsumer;
51  
52  /**
53   * 
54   */
55  public class AccuRevCommandLine
56      implements AccuRev
57  {
58  
59      private static final String[] EMPTY_STRING_ARRAY = new String[] {};
60  
61      private static final File CURRENT_DIR = new File( "." );
62  
63      private ScmLogger logger;
64  
65      private Commandline cl = new Commandline();
66  
67      private StringBuilder commandLines = new StringBuilder();
68  
69      private StringBuilder errorOutput = new StringBuilder();
70  
71      private StreamConsumer systemErr;
72  
73      private String[] hostArgs = EMPTY_STRING_ARRAY;
74  
75      private String[] authArgs = EMPTY_STRING_ARRAY;
76  
77      private String executable = "accurev";
78  
79      private long executableModTime;
80  
81      private String clientVersion;
82  
83      public AccuRevCommandLine()
84      {
85          super();
86          reset();
87      }
88  
89      public AccuRevCommandLine( String host, int port )
90      {
91          this();
92          setServer( host, port );
93      }
94  
95      public void setServer( String host, int port )
96      {
97  
98          if ( host != null )
99          {
100             hostArgs = new String[] { "-H", host + ":" + port };
101         }
102         else
103         {
104             hostArgs = EMPTY_STRING_ARRAY;
105         }
106 
107     }
108 
109     public void setExecutable( String accuRevExe )
110     {
111 
112         executable = accuRevExe;
113         reset();
114     }
115 
116     private boolean executeCommandLine( File basedir, String[] args, Iterable<File> elements, Pattern matchPattern,
117                                         List<File> matchedFiles )
118         throws AccuRevException
119     {
120 
121         FileConsumer stdoutConsumer = new FileConsumer( matchedFiles, matchPattern );
122 
123         return executeCommandLine( basedir, args, elements, stdoutConsumer );
124     }
125 
126     private boolean executeCommandLine( File basedir, String[] args, Iterable<File> elements,
127                                         StreamConsumer stdoutConsumer )
128         throws AccuRevException
129     {
130 
131         setWorkingDirectory( basedir );
132         setCommandLineArgs( args );
133 
134         if ( elements != null )
135         {
136             for ( File file : elements )
137             {
138                 String path = file.getPath();
139                 // Hack for Windows "/./". TODO find a nicer way to handle this.
140                 if ( "\\.".equals( path ) )
141                 {
142                     path = "\\.\\";
143                 }
144                 cl.createArg().setValue( path );
145             }
146         }
147         return executeCommandLine( null, stdoutConsumer ) == 0;
148     }
149 
150     private void setCommandLineArgs( String[] args )
151     {
152 
153         cl.clearArgs();
154 
155         if ( args.length > 0 )
156         {
157             // First arg is the accurev command
158             cl.createArg().setValue( args[0] );
159 
160             // Inject -H <host:port> and -A <token> here
161             cl.addArguments( hostArgs );
162             cl.addArguments( authArgs );
163         }
164 
165         for ( int i = 1; i < args.length; i++ )
166         {
167             cl.createArg().setValue( args[i] );
168         }
169 
170     }
171 
172     private boolean executeCommandLine( String[] args )
173         throws AccuRevException
174     {
175 
176         return executeCommandLine( args, null, null ) == 0;
177     }
178 
179     private int executeCommandLine( String[] args, InputStream stdin, StreamConsumer stdout )
180         throws AccuRevException
181     {
182 
183         setCommandLineArgs( args );
184 
185         return executeCommandLine( stdin, stdout );
186 
187     }
188 
189     private int executeCommandLine( InputStream stdin, StreamConsumer stdout )
190         throws AccuRevException
191     {
192 
193         commandLines.append( cl.toString() );
194         commandLines.append( ';' );
195 
196         if ( getLogger().isDebugEnabled() )
197         {
198             getLogger().debug( cl.toString() );
199         }
200         try
201         {
202 
203             int result = executeCommandLine( cl, stdin, new CommandOutputConsumer( getLogger(), stdout ), systemErr );
204             if ( result != 0 )
205             {
206                 getLogger().debug( "Non zero result - " + result );
207             }
208             return result;
209         }
210         catch ( CommandLineException ex )
211         {
212             throw new AccuRevException( "Error executing command " + cl.toString(), ex );
213         }
214 
215     }
216 
217     /**
218      * Extracted so test class can override
219      * 
220      * @param stdin
221      * @param stdout
222      * @param stderr
223      * @return
224      * @throws CommandLineException
225      */
226     protected int executeCommandLine( Commandline cl, InputStream stdin, CommandOutputConsumer stdout,
227                                       StreamConsumer stderr )
228         throws CommandLineException
229     {
230 
231         int result = CommandLineUtils.executeCommandLine( cl, stdin, stdout, stderr );
232         stdout.waitComplete();
233 
234         return result;
235     }
236 
237     protected Commandline getCommandline()
238     {
239 
240         return cl;
241     }
242 
243     public void reset()
244     {
245 
246         // TODO find out why Commandline allows executable, args etc to be initialised to
247         // null, but not allowing them to be reset to null. This results is weird "clear"
248         // behaviour. It is just safer to start again.
249 
250         cl = new Commandline();
251         commandLines = new StringBuilder();
252         errorOutput = new StringBuilder();
253         systemErr = new ErrorConsumer( getLogger(), errorOutput );
254         cl.getShell().setQuotedArgumentsEnabled( true );
255         cl.setExecutable( executable );
256 
257         try
258         {
259             cl.addSystemEnvironment();
260         }
261         catch ( Exception e )
262         {
263             if ( getLogger().isDebugEnabled() )
264             {
265                 getLogger().debug( "Unable to obtain system environment", e );
266             }
267             else
268             {
269                 getLogger().warn( "Unable to obtain system environment" );
270             }
271         }
272     }
273 
274     /**
275      * {@inheritDoc}
276      */
277     public boolean mkws( String basisStream, String workspaceName, File basedir )
278         throws AccuRevException
279     {
280 
281         setWorkingDirectory( basedir );
282         String[] mkws = { "mkws", "-b", basisStream, "-w", workspaceName, "-l", basedir.getAbsolutePath() };
283 
284         return executeCommandLine( mkws );
285     }
286 
287     /**
288      * {@inheritDoc}
289      */
290     public List<File> update( File baseDir, String transactionId )
291         throws AccuRevException
292     {
293 
294         if ( transactionId == null )
295         {
296             transactionId = "highest";
297         }
298         String[] update = { "update", "-t", transactionId };
299         setWorkingDirectory( baseDir );
300 
301         List<File> updatedFiles = new ArrayList<File>();
302         int ret = executeCommandLine( update, null, new FileConsumer( updatedFiles, FileConsumer.UPDATE_PATTERN ) );
303         return ret == 0 ? updatedFiles : null;
304 
305     }
306 
307     /**
308      * {@inheritDoc}
309      */
310     public List<File> add( File basedir, List<File> elements, String message )
311         throws AccuRevException
312     {
313 
314         if ( StringUtils.isBlank( message ) )
315         {
316             message = AccuRev.DEFAULT_ADD_MESSAGE;
317         }
318 
319         boolean recursive = false;
320 
321         if ( elements == null || elements.isEmpty() )
322         {
323             elements = Collections.singletonList( CURRENT_DIR );
324             recursive = true;
325         }
326         else if ( elements.size() == 1 && elements.toArray()[0].equals( CURRENT_DIR ) )
327         {
328             recursive = true;
329         }
330 
331         List<File> addedFiles = new ArrayList<File>();
332         return executeCommandLine( basedir, new String[] { "add", "-c", message, recursive ? "-R" : null }, elements,
333                                    FileConsumer.ADD_PATTERN, addedFiles ) ? addedFiles : null;
334 
335     }
336 
337     public List<File> defunct( File basedir, List<File> files, String message )
338         throws AccuRevException
339     {
340 
341         if ( StringUtils.isBlank( message ) )
342         {
343             message = AccuRev.DEFAULT_REMOVE_MESSAGE;
344         }
345 
346         if ( files == null || files.isEmpty() )
347         {
348             files = Collections.singletonList( CURRENT_DIR );
349         }
350 
351         ArrayList<File> defunctFiles = new ArrayList<File>();
352         return executeCommandLine( basedir, new String[] { "defunct", "-c", message }, files,
353                                    FileConsumer.DEFUNCT_PATTERN, defunctFiles ) ? defunctFiles : null;
354     }
355 
356     public List<File> promote( File basedir, List<File> files, String message )
357         throws AccuRevException
358     {
359 
360         if ( StringUtils.isBlank( message ) )
361         {
362             message = AccuRev.DEFAULT_PROMOTE_MESSAGE;
363         }
364         List<File> promotedFiles = new ArrayList<File>();
365         return executeCommandLine( basedir, new String[] { "promote", "-K", "-c", message }, files,
366                                    FileConsumer.PROMOTE_PATTERN, promotedFiles ) ? promotedFiles : null;
367 
368     }
369 
370     public String getCommandLines()
371     {
372 
373         return commandLines.toString();
374     }
375 
376     public String getErrorOutput()
377     {
378 
379         return errorOutput.toString();
380     }
381 
382     public void setLogger( ScmLogger logger )
383     {
384         this.logger = logger;
385     }
386 
387     public ScmLogger getLogger()
388     {
389 
390         return logger;
391     }
392 
393     public boolean mkdepot( String depotName )
394         throws AccuRevException
395     {
396 
397         String[] mkdepot = { "mkdepot", "-p", depotName };
398 
399         return executeCommandLine( mkdepot );
400 
401     }
402 
403     public boolean mkstream( String backingStream, String newStreamName )
404         throws AccuRevException
405     {
406         String[] mkstream = { "mkstream", "-b", backingStream, "-s", newStreamName };
407         return executeCommandLine( mkstream );
408 
409     }
410 
411     public boolean promoteStream( String subStream, String commitMessage, List<File> promotedFiles )
412         throws AccuRevException
413     {
414         String[] promote = { "promote", "-s", subStream, "-d" };
415         return executeCommandLine( promote );
416 
417     }
418 
419     /**
420      * {@inheritDoc}
421      */
422     public List<File> promoteAll( File baseDir, String commitMessage )
423         throws AccuRevException
424     {
425         setWorkingDirectory( baseDir );
426         String[] promote = { "promote", "-p", "-K", "-c", commitMessage };
427 
428         List<File> promotedFiles = new ArrayList<File>();
429         int ret = executeCommandLine( promote, null, new FileConsumer( promotedFiles, FileConsumer.PROMOTE_PATTERN ) );
430         return ret == 0 ? promotedFiles : null;
431     }
432 
433     public AccuRevInfo info( File basedir )
434         throws AccuRevException
435     {
436 
437         setWorkingDirectory( basedir );
438         String[] info = { "info" };
439         AccuRevInfo result = new AccuRevInfo( basedir );
440 
441         executeCommandLine( info, null, new InfoConsumer( result ) );
442         return result;
443     }
444 
445     private void setWorkingDirectory( File basedir )
446     {
447 
448         // TODO raise bug against plexus. Null is OK for working directory
449         // but once set to not-null cannot be set back to null!
450         // this is a problem if the old workingdir has been deleted
451         // probably safer to use a new commandline
452 
453         if ( basedir == null )
454         {
455             cl.setWorkingDirectory( "." );
456         }
457         cl.setWorkingDirectory( basedir );
458     }
459 
460     public boolean reactivate( String workSpaceName )
461         throws AccuRevException
462     {
463 
464         String[] reactivate = { "reactivate", "wspace", workSpaceName };
465 
466         return executeCommandLine( reactivate, null, new CommandOutputConsumer( getLogger(), null ) ) == 0;
467 
468     }
469 
470     public boolean rmws( String workSpaceName )
471         throws AccuRevException
472     {
473 
474         String[] rmws = { "rmws", "-s", workSpaceName };
475 
476         return executeCommandLine( rmws );
477 
478     }
479 
480     public String stat( File element )
481         throws AccuRevException
482     {
483 
484         String[] stat = { "stat", "-fx", element.getAbsolutePath() };
485 
486         StatConsumer statConsumer = new StatConsumer( getLogger() );
487         executeCommandLine( stat, null, statConsumer );
488         return statConsumer.getStatus();
489 
490     }
491 
492     public boolean chws( File basedir, String workSpaceName, String newBasisStream )
493         throws AccuRevException
494     {
495 
496         setWorkingDirectory( basedir );
497         return executeCommandLine( new String[] { "chws", "-s", workSpaceName, "-b", newBasisStream, "-l", "." } );
498 
499     }
500 
501     public boolean mksnap( String snapShotName, String basisStream )
502         throws AccuRevException
503     {
504         return executeCommandLine( new String[] { "mksnap", "-s", snapShotName, "-b", basisStream, "-t", "now" } );
505     }
506 
507     public List<File> statTag( String streamName )
508         throws AccuRevException
509     {
510 
511         List<File> taggedFiles = new ArrayList<File>();
512         String[] stat = new String[] { "stat", "-a", "-ffl", "-s", streamName };
513         return executeCommandLine( null, stat, null, FileConsumer.STAT_PATTERN, taggedFiles ) ? taggedFiles : null;
514     }
515 
516     public List<File> stat( File basedir, Collection<File> elements, AccuRevStat statType )
517         throws AccuRevException
518     {
519 
520         boolean recursive = false;
521 
522         if ( elements == null || elements.isEmpty() )
523         {
524             elements = Collections.singletonList( CURRENT_DIR );
525             recursive = true;
526         }
527         else if ( elements.size() == 1 && elements.toArray()[0].equals( CURRENT_DIR ) )
528         {
529             recursive = true;
530         }
531 
532         String[] args = { "stat", "-ffr", statType.getStatArg(), recursive ? "-R" : null };
533 
534         List<File> matchingElements = new ArrayList<File>();
535         boolean ret = executeCommandLine( basedir, args, elements, statType.getMatchPattern(), matchingElements );
536         return ret ? matchingElements : null;
537     }
538 
539     public List<File> pop( File basedir, Collection<File> elements )
540         throws AccuRevException
541     {
542 
543         if ( elements == null || elements.isEmpty() )
544         {
545             elements = Collections.singletonList( CURRENT_DIR );
546         }
547 
548         String[] popws = { "pop", "-R" };
549 
550         List<File> poppedFiles = new ArrayList<File>();
551         boolean ret = executeCommandLine( basedir, popws, elements, FileConsumer.POPULATE_PATTERN, poppedFiles );
552         return ret ? poppedFiles : null;
553     }
554 
555     public List<File> popExternal( File basedir, String versionSpec, String tranSpec, Collection<File> elements )
556         throws AccuRevException
557     {
558 
559         if ( elements == null || elements.isEmpty() )
560         {
561             elements = Collections.singletonList( new File( "/./" ) );
562         }
563 
564         if ( StringUtils.isBlank( tranSpec ) )
565         {
566             tranSpec = "now";
567         }
568 
569         String[] popArgs;
570         if ( AccuRevVersion.isNow( tranSpec ) )
571         {
572             popArgs = new String[] { "pop", "-v", versionSpec, "-L", basedir.getAbsolutePath(), "-R" };
573         }
574         else
575         // this will BARF for pre 4.9.0, but clients are expected to check AccuRevCapability before calling.
576         {
577             popArgs = new String[] { "pop", "-v", versionSpec, "-L", basedir.getAbsolutePath(), "-t", tranSpec, "-R" };
578         }
579 
580         List<File> poppedFiles = new ArrayList<File>();
581         boolean ret = executeCommandLine( basedir, popArgs, elements, FileConsumer.POPULATE_PATTERN, poppedFiles );
582         return ret ? poppedFiles : null;
583     }
584 
585     public CategorisedElements statBackingStream( File basedir, Collection<File> elements )
586         throws AccuRevException
587     {
588 
589         CategorisedElements catElems = new CategorisedElements();
590 
591         if ( elements.isEmpty() )
592         {
593             return catElems;
594         }
595         String[] args = { "stat", "-b", "-ffr" };
596 
597         boolean ret =
598             executeCommandLine( basedir, args, elements, new StatBackingConsumer( catElems.getMemberElements(),
599                                                                                   catElems.getNonMemberElements() ) );
600         return ret ? catElems : null;
601 
602     }
603 
604     public List<Transaction> history( String baseStream, String fromTimeSpec, String toTimeSpec, int count,
605                                       boolean depotHistory, boolean transactionsOnly )
606         throws AccuRevException
607     {
608 
609         String timeSpec = fromTimeSpec;
610 
611         if ( toTimeSpec != null )
612         {
613             timeSpec = timeSpec + "-" + toTimeSpec;
614         }
615 
616         if ( count > 0 )
617         {
618             timeSpec = timeSpec + "." + count;
619         }
620 
621         String[] hist =
622             { "hist", transactionsOnly ? "-ftx" : "-fx", depotHistory ? "-p" : "-s", baseStream, "-t", timeSpec };
623 
624         ArrayList<Transaction> transactions = new ArrayList<Transaction>();
625         HistoryConsumer stdout = new HistoryConsumer( getLogger(), transactions );
626         return executeCommandLine( hist, null, stdout ) == 0 ? transactions : null;
627     }
628 
629     public List<FileDifference> diff( String baseStream, String fromTimeSpec, String toTimeSpec )
630         throws AccuRevException
631     {
632         String timeSpec = fromTimeSpec + "-" + toTimeSpec;
633         String[] diff = { "diff", "-fx", "-a", "-i", "-v", baseStream, "-V", baseStream, "-t", timeSpec };
634 
635         List<FileDifference> results = new ArrayList<FileDifference>();
636         DiffConsumer stdout = new DiffConsumer( getLogger(), results );
637         return executeCommandLine( diff, null, stdout ) < 2 ? results : null;
638     }
639 
640     public boolean login( String user, String password )
641         throws AccuRevException
642     {
643 
644         // TODO Raise bug against plexus commandline - can't set workingdir to null
645         // and will get an error if the working directory is deleted.
646         cl.setWorkingDirectory( "." );
647         authArgs = EMPTY_STRING_ARRAY;
648         AuthTokenConsumer stdout = new AuthTokenConsumer();
649 
650         boolean result;
651         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
652         {
653             if ( StringUtils.isBlank( password ) )
654             {
655                 // Ensure blank is passed in.
656                 password = "\"\"";
657             }
658             String[] login = { "login", "-A", user, password };
659             result = executeCommandLine( login, null, stdout ) == 0;
660         }
661         else
662         {
663             String[] login = { "login", "-A", user };
664             password = StringUtils.clean( password ) + "\n";
665             byte[] bytes = password.getBytes();
666             ByteArrayInputStream stdin = new ByteArrayInputStream( bytes );
667             result = executeCommandLine( login, stdin, stdout ) == 0;
668 
669         }
670 
671         authArgs = new String[] { "-A", stdout.getAuthToken() };
672         return result;
673     }
674 
675     public boolean logout()
676         throws AccuRevException
677     {
678 
679         String[] logout = { "logout" };
680         return executeCommandLine( logout );
681 
682     }
683 
684     public List<BlameLine> annotate( File basedir, File file )
685         throws AccuRevException
686     {
687 
688         String[] annotate = { "annotate", "-ftud" };
689         List<BlameLine> lines = new ArrayList<BlameLine>();
690         AnnotateConsumer stdout = new AnnotateConsumer( lines, getLogger() );
691 
692         return executeCommandLine( basedir, annotate, Collections.singletonList( file ), stdout ) ? lines : null;
693     }
694 
695     public Map<String, WorkSpace> showRefTrees()
696         throws AccuRevException
697     {
698 
699         String[] show = { "show", "-fx", "refs" };
700         Map<String, WorkSpace> refTrees = new HashMap<String, WorkSpace>();
701         WorkSpaceConsumer stdout = new WorkSpaceConsumer( getLogger(), refTrees );
702         return executeCommandLine( show, null, stdout ) == 0 ? refTrees : null;
703     }
704 
705     public Map<String, WorkSpace> showWorkSpaces()
706         throws AccuRevException
707     {
708 
709         String[] show = { "show", "-a", "-fx", "wspaces" };
710         Map<String, WorkSpace> workSpaces = new HashMap<String, WorkSpace>();
711         WorkSpaceConsumer stdout = new WorkSpaceConsumer( getLogger(), workSpaces );
712         return executeCommandLine( show, null, stdout ) == 0 ? workSpaces : null;
713     }
714 
715     public Stream showStream( String stream )
716         throws AccuRevException
717     {
718         String[] show = { "show", "-s", stream, "-fx", "streams" };
719         List<Stream> streams = new ArrayList<Stream>();
720         StreamsConsumer stdout = new StreamsConsumer( getLogger(), streams );
721 
722         return executeCommandLine( show, null, stdout ) == 0 && streams.size() == 1 ? streams.get( 0 ) : null;
723     }
724 
725     public String getExecutable()
726     {
727 
728         return executable;
729     }
730 
731     public String getClientVersion()
732         throws AccuRevException
733     {
734 
735         long lastModified = new File( getExecutable() ).lastModified();
736         if ( clientVersion == null || executableModTime != lastModified )
737         {
738             executableModTime = lastModified;
739 
740             ClientVersionConsumer stdout = new ClientVersionConsumer();
741             executeCommandLine( new String[] {}, null, stdout );
742             clientVersion = stdout.getClientVersion();
743         }
744         return clientVersion;
745 
746     }
747 
748     public boolean syncReplica()
749         throws AccuRevException
750     {
751         return executeCommandLine( new String[] { "replica", "sync" } );
752     }
753 
754 }