View Javadoc
1   package org.apache.maven.scm.provider.bazaar;
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 org.apache.maven.scm.ScmException;
23  import org.apache.maven.scm.ScmFileSet;
24  import org.apache.maven.scm.ScmFileStatus;
25  import org.apache.maven.scm.ScmResult;
26  import org.apache.maven.scm.log.DefaultLog;
27  import org.apache.maven.scm.log.ScmLogger;
28  import org.apache.maven.scm.provider.bazaar.command.BazaarConstants;
29  import org.apache.maven.scm.provider.bazaar.command.BazaarConsumer;
30  import org.codehaus.plexus.util.cli.CommandLineException;
31  import org.codehaus.plexus.util.cli.CommandLineUtils;
32  import org.codehaus.plexus.util.cli.Commandline;
33  
34  import java.io.File;
35  import java.util.ArrayList;
36  import java.util.HashMap;
37  import java.util.List;
38  import java.util.Map;
39  
40  /**
41   * Common code for executing bazaar commands.
42   *
43   * @author <a href="mailto:torbjorn@smorgrav.org">Torbjorn Eikli Smorgrav</a>
44   *
45   */
46  public final class BazaarUtils
47  {
48  
49      private BazaarUtils()
50      {
51      }
52  
53      /**
54       * Map between command and its valid exit codes
55       */
56      private static final Map<String,List<Integer>> EXITCODEMAP = new HashMap<String,List<Integer>>();
57  
58      /**
59       * Default exit codes for entries not in exitCodeMap
60       */
61      private static final List<Integer> DEFAULTEEXITCODES = new ArrayList<Integer>();
62  
63      /** Setup exit codes*/
64      static
65      {
66          DEFAULTEEXITCODES.add( Integer.valueOf( 0 ) );
67  
68          //Diff is different
69          List<Integer> diffExitCodes = new ArrayList<Integer>();
70          diffExitCodes.add( Integer.valueOf( 0 ) ); //No difference
71          diffExitCodes.add( Integer.valueOf( 1 ) ); //Conflicts in merge-like or changes in diff-like
72          diffExitCodes.add( Integer.valueOf( 2 ) ); //Unrepresentable diff changes
73          EXITCODEMAP.put( BazaarConstants.DIFF_CMD, diffExitCodes );
74      }
75  
76      public static ScmResult execute( BazaarConsumer consumer, ScmLogger logger, File workingDir, String[] cmdAndArgs )
77          throws ScmException
78      {
79          try
80          {
81              //Build commandline
82              Commandline cmd = buildCmd( workingDir, cmdAndArgs );
83              if ( logger.isInfoEnabled() )
84              {
85                  logger.info( "EXECUTING: " + cmd );
86              }
87  
88              //Execute command
89              int exitCode = executeCmd( consumer, cmd );
90  
91              //Return result
92              List<Integer> exitCodes = DEFAULTEEXITCODES;
93              if ( EXITCODEMAP.containsKey( cmdAndArgs[0] ) )
94              {
95                  exitCodes = EXITCODEMAP.get( cmdAndArgs[0] );
96              }
97              boolean success = exitCodes.contains( Integer.valueOf( exitCode ) );
98  
99              //On failure (and not due to exceptions) - run diagnostics
100             String providerMsg = "Execution of bazaar command succeded";
101             if ( !success )
102             {
103                 BazaarConfig config = new BazaarConfig( workingDir );
104                 providerMsg = "\nEXECUTION FAILED" + "\n  Execution of cmd : " + cmdAndArgs[0]
105                     + " failed with exit code: " + exitCode + "." + "\n  Working directory was: " + "\n    "
106                     + workingDir.getAbsolutePath() + config.toString( workingDir ) + "\n";
107                 if ( logger.isErrorEnabled() )
108                 {
109                     logger.error( providerMsg );
110                 }
111             }
112 
113             return new ScmResult( cmd.toString(), providerMsg, consumer.getStdErr(), success );
114         }
115         catch ( ScmException se )
116         {
117             String msg =
118                 "EXECUTION FAILED\n  Execution failed before invoking the Bazaar command. Last exception:"
119                     + "\n    " + se.getMessage();
120 
121             //Add nested cause if any
122             if ( se.getCause() != null )
123             {
124                 msg += "\n  Nested exception:" + "\n    " + se.getCause().getMessage();
125             }
126 
127             //log and return
128             if ( logger.isErrorEnabled() )
129             {
130                 logger.error( msg );
131             }
132             throw se;
133         }
134     }
135 
136     static Commandline buildCmd( File workingDir, String[] cmdAndArgs )
137         throws ScmException
138     {
139         Commandline cmd = new Commandline();
140         cmd.setExecutable( BazaarConstants.EXEC );
141         cmd.setWorkingDirectory( workingDir.getAbsolutePath() );
142         cmd.addArguments( cmdAndArgs );
143 
144         if ( !workingDir.exists() )
145         {
146             boolean success = workingDir.mkdirs();
147             if ( !success )
148             {
149                 String msg = "Working directory did not exist" + " and it couldn't be created: " + workingDir;
150                 throw new ScmException( msg );
151             }
152         }
153         return cmd;
154     }
155 
156     static int executeCmd( BazaarConsumer consumer, Commandline cmd )
157         throws ScmException
158     {
159         final int exitCode;
160         try
161         {
162             exitCode = CommandLineUtils.executeCommandLine( cmd, consumer, consumer );
163         }
164         catch ( CommandLineException ex )
165         {
166             throw new ScmException( "Command could not be executed: " + cmd, ex );
167         }
168         return exitCode;
169     }
170 
171     public static ScmResult execute( File workingDir, String[] cmdAndArgs )
172         throws ScmException
173     {
174         ScmLogger logger = new DefaultLog();
175         return execute( new BazaarConsumer( logger ), logger, workingDir, cmdAndArgs );
176     }
177 
178     public static String[] expandCommandLine( String[] cmdAndArgs, ScmFileSet additionalFiles )
179     {
180         List<File> files = additionalFiles.getFileList();
181         String[] cmd = new String[files.size() + cmdAndArgs.length];
182 
183         // Copy command into array
184         System.arraycopy( cmdAndArgs, 0, cmd, 0, cmdAndArgs.length );
185 
186         // Add files as additional parameter into the array
187         for ( int i = 0; i < files.size(); i++ )
188         {
189             String file = files.get( i ).getPath().replace( '\\', File.separatorChar );
190             cmd[i + cmdAndArgs.length] = file;
191         }
192 
193         return cmd;
194     }
195 
196     public static int getCurrentRevisionNumber( ScmLogger logger, File workingDir )
197         throws ScmException
198     {
199 
200         String[] revCmd = new String[]{BazaarConstants.REVNO_CMD};
201         BazaarRevNoConsumer consumer = new BazaarRevNoConsumer( logger );
202         BazaarUtils.execute( consumer, logger, workingDir, revCmd );
203 
204         return consumer.getCurrentRevisionNumber();
205     }
206 
207     /**
208      * Get current (working) revision.
209      * <p/>
210      * Resolve revision to the last integer found in the command output.
211      */
212     private static class BazaarRevNoConsumer
213         extends BazaarConsumer
214     {
215 
216         private int revNo;
217 
218         BazaarRevNoConsumer( ScmLogger logger )
219         {
220             super( logger );
221         }
222 
223         public void doConsume( ScmFileStatus status, String line )
224         {
225             try
226             {
227                 revNo = Integer.valueOf( line ).intValue();
228             }
229             catch ( NumberFormatException e )
230             {
231                 // ignore
232             }
233         }
234 
235         int getCurrentRevisionNumber()
236         {
237             return revNo;
238         }
239     }
240 }