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.ScmFileStatus;
24  import org.apache.maven.scm.log.DefaultLog;
25  import org.apache.maven.scm.provider.bazaar.command.BazaarConstants;
26  import org.apache.maven.scm.provider.bazaar.command.BazaarConsumer;
27  import org.codehaus.plexus.util.cli.Commandline;
28  
29  import java.io.File;
30  import java.util.regex.Matcher;
31  import java.util.regex.Pattern;
32  
33  /**
34   * Check bazaar installation.
35   *
36   * @author <a href="mailto:torbjorn@smorgrav.org">Torbjorn Eikli Smorgrav</a>
37   *
38   */
39  public class BazaarConfig
40  {
41      //Minimum version for the Bazaar SCM
42      private static final float BAZAAR_REQ = 0.7f;
43  
44      private static final float PYTHON_REQ = 2.4f;
45  
46      //Bazaar specific
47      private static final String BAZAAR_VERSION_TAG = "bzr (bazaar-ng) ";
48  
49      private static final String BAZAAR_INSTALL_URL = "'http://bazaar-vcs.org/Installation'";
50  
51      //Python specific
52      private static final String PYTHON_EXEC = "python";
53  
54      private static final String PYTHON_VERSION = "-V";
55  
56      private static final String PYTHON_VERSION_TAG = "Python ";
57  
58      //Python modules
59      private static final String PARAMIKO = "\"import paramiko\"";
60  
61      private static final String CCRYPT = "\"import Crypto\"";
62  
63      private static final String CELEMENTREE = "\"import cElementTree\"";
64  
65      //Configuration to check with default values (not installed)
66      private VersionConsumer bazaarVersion = new VersionConsumer( null );
67  
68      private VersionConsumer pythonVersion = new VersionConsumer( null );
69  
70      private boolean cElementTree = false;
71  
72      private boolean paramiko = false;
73  
74      private boolean cCrypt = false;
75  
76      BazaarConfig( File workingDir )
77      {
78          try
79          {
80              pythonVersion = getPythonVersion( workingDir );
81              paramiko = checkPyModules( workingDir, PARAMIKO ); //does not throw
82              cCrypt = checkPyModules( workingDir, CCRYPT ); //does not throw
83              cElementTree = checkPyModules( workingDir, CELEMENTREE ); //does not throw
84              bazaarVersion = getBazaarVersion( workingDir );
85          }
86          catch ( ScmException e )
87          {
88              //Ignore - Either python and/or bazaar is not installed.
89              //This is already recorded thus we do not generate more info.
90          }
91  
92      }
93  
94      private boolean checkPyModules( File workingDir, String cmd )
95      {
96          PythonConsumer consumer = new PythonConsumer();
97          int exitCode;
98          try
99          {
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 }