001 package org.apache.maven.scm.provider.bazaar;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import org.apache.maven.scm.ScmException;
023 import org.apache.maven.scm.ScmFileSet;
024 import org.apache.maven.scm.ScmFileStatus;
025 import org.apache.maven.scm.ScmResult;
026 import org.apache.maven.scm.log.DefaultLog;
027 import org.apache.maven.scm.log.ScmLogger;
028 import org.apache.maven.scm.provider.bazaar.command.BazaarConstants;
029 import org.apache.maven.scm.provider.bazaar.command.BazaarConsumer;
030 import org.codehaus.plexus.util.cli.CommandLineException;
031 import org.codehaus.plexus.util.cli.CommandLineUtils;
032 import org.codehaus.plexus.util.cli.Commandline;
033
034 import java.io.File;
035 import java.util.ArrayList;
036 import java.util.HashMap;
037 import java.util.List;
038 import java.util.Map;
039
040 /**
041 * Common code for executing bazaar commands.
042 *
043 * @author <a href="mailto:torbjorn@smorgrav.org">Torbj�rn Eikli Sm�rgrav</a>
044 *
045 */
046 public final class BazaarUtils
047 {
048
049 private BazaarUtils()
050 {
051 }
052
053 /**
054 * Map between command and its valid exit codes
055 */
056 private static final Map<String,List<Integer>> EXITCODEMAP = new HashMap<String,List<Integer>>();
057
058 /**
059 * Default exit codes for entries not in exitCodeMap
060 */
061 private static final List<Integer> DEFAULTEEXITCODES = new ArrayList<Integer>();
062
063 /** Setup exit codes*/
064 static
065 {
066 DEFAULTEEXITCODES.add( Integer.valueOf( 0 ) );
067
068 //Diff is different
069 List<Integer> diffExitCodes = new ArrayList<Integer>();
070 diffExitCodes.add( Integer.valueOf( 0 ) ); //No difference
071 diffExitCodes.add( Integer.valueOf( 1 ) ); //Conflicts in merge-like or changes in diff-like
072 diffExitCodes.add( Integer.valueOf( 2 ) ); //Unrepresentable diff changes
073 EXITCODEMAP.put( BazaarConstants.DIFF_CMD, diffExitCodes );
074 }
075
076 public static ScmResult execute( BazaarConsumer consumer, ScmLogger logger, File workingDir, String[] cmdAndArgs )
077 throws ScmException
078 {
079 try
080 {
081 //Build commandline
082 Commandline cmd = buildCmd( workingDir, cmdAndArgs );
083 if ( logger.isInfoEnabled() )
084 {
085 logger.info( "EXECUTING: " + cmd );
086 }
087
088 //Execute command
089 int exitCode = executeCmd( consumer, cmd );
090
091 //Return result
092 List<Integer> exitCodes = DEFAULTEEXITCODES;
093 if ( EXITCODEMAP.containsKey( cmdAndArgs[0] ) )
094 {
095 exitCodes = EXITCODEMAP.get( cmdAndArgs[0] );
096 }
097 boolean success = exitCodes.contains( Integer.valueOf( exitCode ) );
098
099 //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 }