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.ScmFileStatus;
024 import org.apache.maven.scm.log.DefaultLog;
025 import org.apache.maven.scm.provider.bazaar.command.BazaarConstants;
026 import org.apache.maven.scm.provider.bazaar.command.BazaarConsumer;
027 import org.codehaus.plexus.util.cli.Commandline;
028
029 import java.io.File;
030 import java.util.regex.Matcher;
031 import java.util.regex.Pattern;
032
033 /**
034 * Check bazaar installation.
035 *
036 * @author <a href="mailto:torbjorn@smorgrav.org">Torbj�rn Eikli Sm�rgrav</a>
037 *
038 */
039 public class BazaarConfig
040 {
041 //Minimum version for the Bazaar SCM
042 private static final float BAZAAR_REQ = 0.7f;
043
044 private static final float PYTHON_REQ = 2.4f;
045
046 //Bazaar specific
047 private static final String BAZAAR_VERSION_TAG = "bzr (bazaar-ng) ";
048
049 private static final String BAZAAR_INSTALL_URL = "'http://bazaar-vcs.org/Installation'";
050
051 //Python specific
052 private static final String PYTHON_EXEC = "python";
053
054 private static final String PYTHON_VERSION = "-V";
055
056 private static final String PYTHON_VERSION_TAG = "Python ";
057
058 //Python modules
059 private static final String PARAMIKO = "\"import paramiko\"";
060
061 private static final String CCRYPT = "\"import Crypto\"";
062
063 private static final String CELEMENTREE = "\"import cElementTree\"";
064
065 //Configuration to check with default values (not installed)
066 private VersionConsumer bazaarVersion = new VersionConsumer( null );
067
068 private VersionConsumer pythonVersion = new VersionConsumer( null );
069
070 private boolean cElementTree = false;
071
072 private boolean paramiko = false;
073
074 private boolean cCrypt = false;
075
076 BazaarConfig( File workingDir )
077 {
078 try
079 {
080 pythonVersion = getPythonVersion( workingDir );
081 paramiko = checkPyModules( workingDir, PARAMIKO ); //does not throw
082 cCrypt = checkPyModules( workingDir, CCRYPT ); //does not throw
083 cElementTree = checkPyModules( workingDir, CELEMENTREE ); //does not throw
084 bazaarVersion = getBazaarVersion( workingDir );
085 }
086 catch ( ScmException e )
087 {
088 //Ignore - Either python and/or bazaar is not installed.
089 //This is already recorded thus we do not generate more info.
090 }
091
092 }
093
094 private boolean checkPyModules( File workingDir, String cmd )
095 {
096 PythonConsumer consumer = new PythonConsumer();
097 int exitCode;
098 try
099 {
100 Commandline cmdLine = buildPythonCmd( workingDir, new String[]{"-c", cmd} );
101 exitCode = BazaarUtils.executeCmd( consumer, cmdLine );
102 }
103 catch ( ScmException e )
104 {
105 //Ignore - error here is likly to manifest itself when checking python anyway.
106 exitCode = -1;
107 }
108
109 return exitCode == 0 && consumer.getConsumedAndClear().equals( "" );
110 }
111
112 /**
113 * @return True if one can run basic bazaar commands
114 */
115 private boolean isInstalled()
116 {
117 return pythonVersion.isVersionOk( PYTHON_REQ ) && bazaarVersion.isVersionOk( BAZAAR_REQ );
118 }
119
120 /**
121 * @return True if all modules for bazaar are installed.
122 */
123 private boolean isComplete()
124 {
125 return isInstalled() && cElementTree && paramiko && cCrypt;
126 }
127
128 public static VersionConsumer getBazaarVersion( File workingDir )
129 throws ScmException
130 {
131 String[] versionCmd = new String[]{BazaarConstants.VERSION};
132 VersionConsumer consumer = new VersionConsumer( BAZAAR_VERSION_TAG );
133 Commandline cmd = BazaarUtils.buildCmd( workingDir, versionCmd );
134
135 // Execute command
136 BazaarUtils.executeCmd( consumer, cmd );
137
138 // Return result
139 return consumer;
140 }
141
142 public static VersionConsumer getPythonVersion( File workingDir )
143 throws ScmException
144 {
145 String[] versionCmd = new String[]{PYTHON_VERSION};
146 VersionConsumer consumer = new VersionConsumer( PYTHON_VERSION_TAG );
147 Commandline cmd = buildPythonCmd( workingDir, versionCmd );
148
149 // Execute command
150 BazaarUtils.executeCmd( consumer, cmd );
151
152 // Return result
153 return consumer;
154 }
155
156 private static Commandline buildPythonCmd( File workingDir, String[] cmdAndArgs )
157 throws ScmException
158 {
159 Commandline cmd = new Commandline();
160 cmd.setExecutable( PYTHON_EXEC );
161 cmd.setWorkingDirectory( workingDir.getAbsolutePath() );
162 cmd.addArguments( cmdAndArgs );
163
164 if ( !workingDir.exists() )
165 {
166 boolean success = workingDir.mkdirs();
167 if ( !success )
168 {
169 String msg = "Working directory did not exist" + " and it couldn't be created: " + workingDir;
170 throw new ScmException( msg );
171 }
172 }
173 return cmd;
174 }
175
176 /**
177 * Get version of the executable.
178 * Version is resolved to the last match of a defined regexp in the command output.
179 */
180 private static class VersionConsumer
181 extends BazaarConsumer
182 {
183
184 private static final Pattern VERSION_PATTERN = Pattern.compile( "[\\d]+.?[\\d]*" );
185
186 private final String versionTag;
187
188 private String versionStr = "NA";
189
190 private float version = -1;
191
192 VersionConsumer( String aVersionTag )
193 {
194 super( new DefaultLog() );
195 this.versionTag = aVersionTag;
196 }
197
198 public void doConsume( ScmFileStatus status, String line )
199 {
200 if ( line.startsWith( versionTag ) )
201 {
202 versionStr = line.substring( versionTag.length() );
203 }
204 }
205
206 String getVersion()
207 {
208 return versionStr;
209 }
210
211 boolean isVersionOk( float min )
212 {
213
214 Matcher matcher = VERSION_PATTERN.matcher( versionStr );
215 if ( matcher.find() )
216 {
217 String subStr = versionStr.substring( matcher.start(), matcher.end() );
218 try
219 {
220 version = Float.valueOf( subStr ).floatValue();
221 }
222 catch ( NumberFormatException e )
223 {
224 //Print diagnostics and continue (this is not a major error)
225 if ( getLogger().isErrorEnabled() )
226 {
227 getLogger().error( "Regexp for version did not result in a number: " + subStr, e );
228 }
229 }
230 }
231
232 return min <= version;
233 }
234 }
235
236 private static class PythonConsumer
237 extends BazaarConsumer
238 {
239
240 private String consumed = "";
241
242 PythonConsumer()
243 {
244 super( new DefaultLog() );
245 }
246
247 public void doConsume( ScmFileStatus status, String line )
248 {
249 consumed = line;
250 }
251
252 String getConsumedAndClear()
253 {
254 String tmp = consumed;
255 consumed = "";
256 return tmp;
257 }
258 }
259
260 private String getInstalledStr()
261 {
262 if ( isComplete() )
263 {
264 return "valid and complete.";
265 }
266 return ( isInstalled() ? "incomplete. " : "invalid. " ) + "Consult " + BAZAAR_INSTALL_URL;
267 }
268
269 public String toString( File workingDir )
270 {
271 boolean bzrOk = bazaarVersion.isVersionOk( BAZAAR_REQ );
272 boolean pyOk = pythonVersion.isVersionOk( PYTHON_REQ );
273 return "\n Your Bazaar installation seems to be " + getInstalledStr() + "\n Python version: "
274 + pythonVersion.getVersion() + ( pyOk ? " (OK)" : " (May be INVALID)" ) + "\n Bazaar version: "
275 + bazaarVersion.getVersion() + ( bzrOk ? " (OK)" : " (May be INVALID)" ) + "\n Paramiko installed: "
276 + paramiko + " (For remote access eg. sftp) " + "\n cCrypt installed: " + cCrypt
277 + " (For remote access eg. sftp) " + "\n cElementTree installed: " + cElementTree + " (Not mandatory) "
278 + "\n";
279 }
280 }