View Javadoc
1   package org.apache.maven.scm.provider.clearcase.command.checkout;
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 java.io.File;
23  import java.io.FileWriter;
24  import java.io.IOException;
25  import java.net.InetAddress;
26  import java.net.UnknownHostException;
27  
28  import org.apache.maven.scm.ScmException;
29  import org.apache.maven.scm.ScmFileSet;
30  import org.apache.maven.scm.ScmVersion;
31  import org.apache.maven.scm.command.checkout.AbstractCheckOutCommand;
32  import org.apache.maven.scm.command.checkout.CheckOutScmResult;
33  import org.apache.maven.scm.provider.ScmProviderRepository;
34  import org.apache.maven.scm.provider.clearcase.command.ClearCaseCommand;
35  import org.apache.maven.scm.provider.clearcase.repository.ClearCaseScmProviderRepository;
36  import org.apache.maven.scm.providers.clearcase.settings.Settings;
37  import org.codehaus.plexus.util.FileUtils;
38  import org.codehaus.plexus.util.StringUtils;
39  import org.codehaus.plexus.util.cli.CommandLineException;
40  import org.codehaus.plexus.util.cli.CommandLineUtils;
41  import org.codehaus.plexus.util.cli.Commandline;
42  
43  /**
44   * @author <a href="mailto:wim.deblauwe@gmail.com">Wim Deblauwe</a>
45   * @author <a href="mailto:frederic.mura@laposte.net">Frederic Mura</a>
46   *
47   */
48  public class ClearCaseCheckOutCommand
49      extends AbstractCheckOutCommand
50      implements ClearCaseCommand
51  {
52      private Settings settings = null;
53  
54      // ----------------------------------------------------------------------
55      // AbstractCheckOutCommand Implementation
56      // ----------------------------------------------------------------------
57  
58      /** {@inheritDoc} */
59      protected CheckOutScmResult executeCheckOutCommand( ScmProviderRepository repository, ScmFileSet fileSet,
60                                                          ScmVersion version, boolean recursive )
61          throws ScmException
62      {
63          if ( getLogger().isDebugEnabled() )
64          {
65              getLogger().debug( "executing checkout command..." );
66          }
67          ClearCaseScmProviderRepository repo = (ClearCaseScmProviderRepository) repository;
68          File workingDirectory = fileSet.getBasedir();
69  
70          if ( version != null && getLogger().isDebugEnabled() )
71          {
72              getLogger().debug( version.getType() + ": " + version.getName() );
73          }
74  
75          if ( getLogger().isDebugEnabled() )
76          {
77              getLogger().debug( "Running with CLEARCASE " + settings.getClearcaseType() );
78          }
79  
80          ClearCaseCheckOutConsumer consumer = new ClearCaseCheckOutConsumer( getLogger() );
81  
82          CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
83  
84          int exitCode;
85  
86          Commandline cl;
87          String projectDirectory = "";
88  
89          try
90          {
91              // Since clearcase only wants to checkout to a non-existent directory, first delete the working dir
92              // if it already exists
93              FileUtils.deleteDirectory( workingDirectory );
94              // First create the view
95              String viewName = getUniqueViewName( repo, workingDirectory.getAbsolutePath() );
96              String streamIdentifier = getStreamIdentifier( repo.getStreamName(), repo.getVobName() );
97              cl = createCreateViewCommandLine( workingDirectory, viewName, streamIdentifier );
98              if ( getLogger().isInfoEnabled() )
99              {
100                 getLogger().info( "Executing: " + cl.getWorkingDirectory().getAbsolutePath() + ">>" + cl.toString() );
101             }
102             exitCode =
103                 CommandLineUtils.executeCommandLine( cl, new CommandLineUtils.StringStreamConsumer(), stderr );
104 
105             if ( exitCode == 0 )
106             {
107                 File configSpecLocation;
108 
109                 if ( !repo.isAutoConfigSpec() )
110                 {
111                     configSpecLocation = repo.getConfigSpec();
112                     if ( version != null && StringUtils.isNotEmpty( version.getName() ) )
113                     {
114                         // Another config spec is needed in this case.
115                         //
116                         // One option how to implement this would be to use a name convention for the config specs,
117                         // e.g. the tag name could be appended to the original config spec name.
118                         // If the config spec from the SCM URL would be \\myserver\configspecs\someproj.txt
119                         // and the tag name would be mytag, the new config spec location could be
120                         // \\myserver\configspecs\someproj-mytag.txt
121                         //
122                         throw new UnsupportedOperationException(
123                             "Building on a label not supported with user-specified config specs" );
124                     }
125                 }
126                 else
127                 {
128 
129                     // write config spec to temp file
130                     String configSpec;
131                     if ( !repo.hasElements() )
132                     {
133                         configSpec = createConfigSpec( repo.getLoadDirectory(), version );
134                     }
135                     else
136                     {
137                         configSpec = createConfigSpec( repo.getLoadDirectory(), repo.getElementName(), version );
138                     }
139                     if ( getLogger().isInfoEnabled() )
140                     {
141                         getLogger().info( "Created config spec for view '" + viewName + "':\n" + configSpec );
142                     }
143                     configSpecLocation = writeTemporaryConfigSpecFile( configSpec, viewName );
144 
145                     // When checking out from ClearCase, the directory structure of the
146                     // SCM system is repeated within the checkout directory. E.g. if you check out the
147                     // project "my/project" to "/some/dir", the project sources are actually checked out
148                     // to "my/project/some/dir".
149                     projectDirectory = repo.getLoadDirectory();
150                     // strip off leading / to make the path relative
151                     if ( projectDirectory.startsWith( "/" ) )
152                     {
153                         projectDirectory = projectDirectory.substring( 1 );
154                     }
155                 }
156 
157                 cl = createUpdateConfigSpecCommandLine( workingDirectory, configSpecLocation, viewName );
158 
159                 if ( getLogger().isInfoEnabled() )
160                 {
161                     getLogger().info( "Executing: " + cl.getWorkingDirectory().getAbsolutePath() + ">>" + cl.toString() );
162                 }
163                 exitCode = CommandLineUtils.executeCommandLine( cl, consumer, stderr );
164 
165             }
166         }
167         catch ( CommandLineException ex )
168         {
169             throw new ScmException( "Error while executing clearcase command.", ex );
170         }
171         catch ( IOException ex )
172         {
173             throw new ScmException( "Error while deleting working directory.", ex );
174         }
175 
176         if ( exitCode != 0 )
177         {
178             return new CheckOutScmResult( cl.toString(), "The cleartool command failed.", stderr.getOutput(), false );
179         }
180 
181         return new CheckOutScmResult( cl.toString(), consumer.getCheckedOutFiles(), projectDirectory );
182     }
183 
184     // ----------------------------------------------------------------------
185     //
186     // ----------------------------------------------------------------------
187 
188     /**
189      * Creates a temporary config spec file with the given contents that will be
190      * deleted on VM exit.
191      *
192      * @param configSpecContents The contents for the file
193      * @param viewName           The name of the view; used to determine an appropriate file
194      *                           name
195      * @throws IOException
196      */
197     protected File writeTemporaryConfigSpecFile( String configSpecContents, String viewName )
198         throws IOException
199     {
200         File configSpecLocation = File.createTempFile( "configspec-" + viewName, ".txt" );
201         FileWriter fw = new FileWriter( configSpecLocation );
202         try
203         {
204             fw.write( configSpecContents );
205         }
206         finally
207         {
208             try
209             {
210                 fw.close();
211             }
212             catch ( IOException e )
213             {
214                 // ignore
215             }
216         }
217         configSpecLocation.deleteOnExit();
218         return configSpecLocation;
219     }
220 
221     /**
222      * Creates a config spec that loads the given loadDirectory and uses the
223      * given version tag
224      *
225      * @param loadDirectory the VOB directory to be loaded
226      * @param version       ClearCase label type; notice that branch types are not
227      *                      supported
228      * @return Config Spec as String
229      */
230     protected String createConfigSpec( String loadDirectory, ScmVersion version )
231     {
232         // create config spec
233         StringBuilder configSpec = new StringBuilder();
234         configSpec.append( "element * CHECKEDOUT\n" );
235         if ( version != null && StringUtils.isNotEmpty( version.getName() ) )
236         {
237             configSpec.append( "element * " + version.getName() + "\n" );
238             configSpec.append( "element -directory * /main/LATEST\n" );
239             // configSpec.append( "element * /main/QualityControl_INT/RAD7_Migration/LATEST\n" );
240         }
241         else
242         {
243             configSpec.append( "element * /main/LATEST\n" );
244         }
245         configSpec.append( "load " + loadDirectory + "\n" );
246         return configSpec.toString();
247     }
248 
249     protected String createConfigSpec( String loadDirectory, String elementName, ScmVersion version )
250     {
251         // create config spec
252         StringBuilder configSpec = new StringBuilder();
253         configSpec.append( "element * CHECKEDOUT\n" );
254         if ( version != null && StringUtils.isNotEmpty( version.getName() ) )
255         {
256             configSpec.append( "element * " + version.getName() + "\n" );
257             configSpec.append( "element * " + elementName + "\n" );
258         }
259         else
260         {
261             configSpec.append( "element * /main/LATEST\n" );
262         }
263         configSpec.append( "load " + loadDirectory + "\n" );
264         return configSpec.toString();
265     }
266 
267 //    private static Commandline createDeleteViewCommandLine( ClearCaseScmProviderRepository repository,
268 //                                                            File workingDirectory )
269 //    {
270 //        Commandline command = new Commandline();
271 //
272 //        command.setWorkingDirectory( workingDirectory.getAbsolutePath() );
273 //
274 //        command.setExecutable( "cleartool" );
275 //
276 //        command.createArg().setValue( "rmview" );
277 //        command.createArg().setValue( "-force" );
278 //        command.createArg().setValue( "-tag" );
279 //        if ( isClearCaseLT() )
280 //        {
281 //            command.createArg().setValue( getViewStore() );
282 //        }
283 //        else
284 //        {
285 //            command.createArg().setValue( getUniqueViewName( repository, workingDirectory.getAbsolutePath() ) );
286 //        }
287 //
288 //        return command;
289 //    }
290 
291     protected Commandline createCreateViewCommandLine( File workingDirectory, String viewName, String streamIdentifier )
292         throws IOException
293     {
294         Commandline command = new Commandline();
295 
296         // We have to execute from 1 level up from the working dir, since we had to delete the working dir
297         command.setWorkingDirectory( workingDirectory.getParentFile().getAbsolutePath() );
298 
299         command.setExecutable( "cleartool" );
300 
301         command.createArg().setValue( "mkview" );
302         command.createArg().setValue( "-snapshot" );
303         command.createArg().setValue( "-tag" );
304         command.createArg().setValue( viewName );
305 
306         if ( isClearCaseUCM() )
307         {
308             command.createArg().setValue( "-stream" );
309             command.createArg().setValue( streamIdentifier );
310         }
311 
312         if ( !isClearCaseLT() )
313         {
314             if ( useVWS() )
315             {
316                 command.createArg().setValue( "-vws" );
317                 command.createArg().setValue( getViewStore() + viewName + ".vws" );
318             }
319         }
320 
321         command.createArg().setValue( workingDirectory.getCanonicalPath() );
322 
323         return command;
324     }
325 
326     /**
327      * Format the stream identifier for ClearCaseUCM
328      * @param streamName
329      * @param vobName
330      * @return the formatted stream identifier if the two parameter are not null
331      */
332     protected String getStreamIdentifier( String streamName, String vobName )
333     {
334         if ( streamName == null || vobName == null )
335         {
336             return null;
337         }
338         return "stream:" + streamName + "@" + vobName;
339     }
340 
341     protected Commandline createUpdateConfigSpecCommandLine( File workingDirectory, File configSpecLocation,
342                                                                     String viewName )
343     {
344         Commandline command = new Commandline();
345 
346         command.setWorkingDirectory( workingDirectory.getAbsolutePath() );
347 
348         command.setExecutable( "cleartool" );
349 
350         command.createArg().setValue( "setcs" );
351         command.createArg().setValue( "-tag" );
352         command.createArg().setValue( viewName );
353         command.createArg().setValue( configSpecLocation.getAbsolutePath() );
354 
355         return command;
356 
357     }
358 
359     private String getUniqueViewName( ClearCaseScmProviderRepository repository, String absolutePath )
360     {
361         String uniqueId;
362         int lastIndexBack = absolutePath.lastIndexOf( '\\' );
363         int lastIndexForward = absolutePath.lastIndexOf( '/' );
364         if ( lastIndexBack != -1 )
365         {
366             uniqueId = absolutePath.substring( lastIndexBack + 1 );
367         }
368         else
369         {
370             uniqueId = absolutePath.substring( lastIndexForward + 1 );
371         }
372         return repository.getViewName( uniqueId );
373     }
374 
375     protected String getViewStore()
376     {
377         String result = null;
378 
379         if ( settings.getViewstore() != null )
380         {
381             result = settings.getViewstore();
382         }
383 
384         if ( result == null )
385         {
386             result = "\\\\" + getHostName() + "\\viewstore\\";
387         }
388         else
389         {
390             // If ClearCase LT are use, the View store is identify by the
391             // username.
392             if ( isClearCaseLT() )
393             {
394                 result = result + getUserName() + "\\";
395             }
396         }
397         return result;
398     }
399 
400     protected boolean isClearCaseLT()
401     {
402         return ClearCaseScmProviderRepository.CLEARCASE_LT.equals( settings.getClearcaseType() );
403     }
404 
405     protected boolean isClearCaseUCM()
406     {
407         return ClearCaseScmProviderRepository.CLEARCASE_UCM.equals( settings.getClearcaseType() );
408     }
409 
410     /**
411      * @return the value of the setting property 'useVWS'
412      */
413     protected boolean useVWS()
414     {
415         return settings.isUseVWSParameter();
416     }
417 
418     private String getHostName()
419     {
420         String hostname;
421         try
422         {
423             hostname = InetAddress.getLocalHost().getHostName();
424         }
425         catch ( UnknownHostException e )
426         {
427             // Should never happen
428             throw new RuntimeException( e );
429         }
430         return hostname;
431     }
432 
433     private String getUserName()
434     {
435         String username;
436         username = System.getProperty( "user.name" );
437         return username;
438     }
439 
440     public void setSettings( Settings settings )
441     {
442         this.settings = settings;
443     }
444 }