001package 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 022import org.apache.maven.scm.ScmException; 023import org.apache.maven.scm.ScmFileSet; 024import org.apache.maven.scm.ScmFileStatus; 025import org.apache.maven.scm.ScmResult; 026import org.apache.maven.scm.log.DefaultLog; 027import org.apache.maven.scm.log.ScmLogger; 028import org.apache.maven.scm.provider.bazaar.command.BazaarConstants; 029import org.apache.maven.scm.provider.bazaar.command.BazaarConsumer; 030import org.codehaus.plexus.util.cli.CommandLineException; 031import org.codehaus.plexus.util.cli.CommandLineUtils; 032import org.codehaus.plexus.util.cli.Commandline; 033 034import java.io.File; 035import java.util.ArrayList; 036import java.util.HashMap; 037import java.util.List; 038import java.util.Map; 039 040/** 041 * Common code for executing bazaar commands. 042 * 043 * @author <a href="mailto:torbjorn@smorgrav.org">Torbjorn Eikli Smorgrav</a> 044 * 045 */ 046public 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}