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()
162                                       + ">>" + cl.toString() );
163                 }
164                 exitCode = CommandLineUtils.executeCommandLine( cl, consumer, stderr );
165 
166             }
167         }
168         catch ( CommandLineException ex )
169         {
170             throw new ScmException( "Error while executing clearcase command.", ex );
171         }
172         catch ( IOException ex )
173         {
174             throw new ScmException( "Error while deleting working directory.", ex );
175         }
176 
177         if ( exitCode != 0 )
178         {
179             return new CheckOutScmResult( cl.toString(), "The cleartool command failed.", stderr.getOutput(), false );
180         }
181 
182         return new CheckOutScmResult( cl.toString(), consumer.getCheckedOutFiles(), projectDirectory );
183     }
184 
185     // ----------------------------------------------------------------------
186     //
187     // ----------------------------------------------------------------------
188 
189     /**
190      * Creates a temporary config spec file with the given contents that will be
191      * deleted on VM exit.
192      *
193      * @param configSpecContents The contents for the file
194      * @param viewName           The name of the view; used to determine an appropriate file
195      *                           name
196      * @throws IOException
197      */
198     protected File writeTemporaryConfigSpecFile( String configSpecContents, String viewName )
199         throws IOException
200     {
201         File configSpecLocation = File.createTempFile( "configspec-" + viewName, ".txt" );
202         FileWriter fw = new FileWriter( configSpecLocation );
203         try
204         {
205             fw.write( configSpecContents );
206         }
207         finally
208         {
209             try
210             {
211                 fw.close();
212             }
213             catch ( IOException e )
214             {
215                 // ignore
216             }
217         }
218         configSpecLocation.deleteOnExit();
219         return configSpecLocation;
220     }
221 
222     /**
223      * Creates a config spec that loads the given loadDirectory and uses the
224      * given version tag
225      *
226      * @param loadDirectory the VOB directory to be loaded
227      * @param version       ClearCase label type; notice that branch types are not
228      *                      supported
229      * @return Config Spec as String
230      */
231     protected String createConfigSpec( String loadDirectory, ScmVersion version )
232     {
233         // create config spec
234         StringBuilder configSpec = new StringBuilder();
235         configSpec.append( "element * CHECKEDOUT\n" );
236         if ( version != null && StringUtils.isNotEmpty( version.getName() ) )
237         {
238             configSpec.append( "element * " + version.getName() + "\n" );
239             configSpec.append( "element -directory * /main/LATEST\n" );
240             // configSpec.append( "element * /main/QualityControl_INT/RAD7_Migration/LATEST\n" );
241         }
242         else
243         {
244             configSpec.append( "element * /main/LATEST\n" );
245         }
246         configSpec.append( "load " + loadDirectory + "\n" );
247         return configSpec.toString();
248     }
249 
250     protected String createConfigSpec( String loadDirectory, String elementName, ScmVersion version )
251     {
252         // create config spec
253         StringBuilder configSpec = new StringBuilder();
254         configSpec.append( "element * CHECKEDOUT\n" );
255         if ( version != null && StringUtils.isNotEmpty( version.getName() ) )
256         {
257             configSpec.append( "element * " + version.getName() + "\n" );
258             configSpec.append( "element * " + elementName + "\n" );
259         }
260         else
261         {
262             configSpec.append( "element * /main/LATEST\n" );
263         }
264         configSpec.append( "load " + loadDirectory + "\n" );
265         return configSpec.toString();
266     }
267 
268 //    private static Commandline createDeleteViewCommandLine( ClearCaseScmProviderRepository repository,
269 //                                                            File workingDirectory )
270 //    {
271 //        Commandline command = new Commandline();
272 //
273 //        command.setWorkingDirectory( workingDirectory.getAbsolutePath() );
274 //
275 //        command.setExecutable( "cleartool" );
276 //
277 //        command.createArg().setValue( "rmview" );
278 //        command.createArg().setValue( "-force" );
279 //        command.createArg().setValue( "-tag" );
280 //        if ( isClearCaseLT() )
281 //        {
282 //            command.createArg().setValue( getViewStore() );
283 //        }
284 //        else
285 //        {
286 //            command.createArg().setValue( getUniqueViewName( repository, workingDirectory.getAbsolutePath() ) );
287 //        }
288 //
289 //        return command;
290 //    }
291 
292     protected Commandline createCreateViewCommandLine( File workingDirectory, String viewName, String streamIdentifier )
293         throws IOException
294     {
295         Commandline command = new Commandline();
296 
297         // We have to execute from 1 level up from the working dir, since we had to delete the working dir
298         command.setWorkingDirectory( workingDirectory.getParentFile().getAbsolutePath() );
299 
300         command.setExecutable( "cleartool" );
301 
302         command.createArg().setValue( "mkview" );
303         command.createArg().setValue( "-snapshot" );
304         command.createArg().setValue( "-tag" );
305         command.createArg().setValue( viewName );
306 
307         if ( isClearCaseUCM() )
308         {
309             command.createArg().setValue( "-stream" );
310             command.createArg().setValue( streamIdentifier );
311         }
312 
313         if ( !isClearCaseLT() )
314         {
315             if ( useVWS() )
316             {
317                 command.createArg().setValue( "-vws" );
318                 command.createArg().setValue( getViewStore() + viewName + ".vws" );
319             }
320         }
321 
322         command.createArg().setValue( workingDirectory.getCanonicalPath() );
323 
324         return command;
325     }
326 
327     /**
328      * Format the stream identifier for ClearCaseUCM
329      * @param streamName
330      * @param vobName
331      * @return the formatted stream identifier if the two parameter are not null
332      */
333     protected String getStreamIdentifier( String streamName, String vobName )
334     {
335         if ( streamName == null || vobName == null )
336         {
337             return null;
338         }
339         return "stream:" + streamName + "@" + vobName;
340     }
341 
342     protected Commandline createUpdateConfigSpecCommandLine( File workingDirectory, File configSpecLocation,
343                                                                     String viewName )
344     {
345         Commandline command = new Commandline();
346 
347         command.setWorkingDirectory( workingDirectory.getAbsolutePath() );
348 
349         command.setExecutable( "cleartool" );
350 
351         command.createArg().setValue( "setcs" );
352         command.createArg().setValue( "-tag" );
353         command.createArg().setValue( viewName );
354         command.createArg().setValue( configSpecLocation.getAbsolutePath() );
355 
356         return command;
357 
358     }
359 
360     private String getUniqueViewName( ClearCaseScmProviderRepository repository, String absolutePath )
361     {
362         String uniqueId;
363         int lastIndexBack = absolutePath.lastIndexOf( '\\' );
364         int lastIndexForward = absolutePath.lastIndexOf( '/' );
365         if ( lastIndexBack != -1 )
366         {
367             uniqueId = absolutePath.substring( lastIndexBack + 1 );
368         }
369         else
370         {
371             uniqueId = absolutePath.substring( lastIndexForward + 1 );
372         }
373         return repository.getViewName( uniqueId );
374     }
375 
376     protected String getViewStore()
377     {
378         String result = null;
379 
380         if ( settings.getViewstore() != null )
381         {
382             result = settings.getViewstore();
383         }
384 
385         if ( result == null )
386         {
387             result = "\\\\" + getHostName() + "\\viewstore\\";
388         }
389         else
390         {
391             // If ClearCase LT are use, the View store is identify by the
392             // username.
393             if ( isClearCaseLT() )
394             {
395                 result = result + getUserName() + "\\";
396             }
397         }
398         return result;
399     }
400 
401     protected boolean isClearCaseLT()
402     {
403         return ClearCaseScmProviderRepository.CLEARCASE_LT.equals( settings.getClearcaseType() );
404     }
405 
406     protected boolean isClearCaseUCM()
407     {
408         return ClearCaseScmProviderRepository.CLEARCASE_UCM.equals( settings.getClearcaseType() );
409     }
410 
411     /**
412      * @return the value of the setting property 'useVWS'
413      */
414     protected boolean useVWS()
415     {
416         return settings.isUseVWSParameter();
417     }
418 
419     private String getHostName()
420     {
421         String hostname;
422         try
423         {
424             hostname = InetAddress.getLocalHost().getHostName();
425         }
426         catch ( UnknownHostException e )
427         {
428             // Should never happen
429             throw new RuntimeException( e );
430         }
431         return hostname;
432     }
433 
434     private String getUserName()
435     {
436         String username;
437         username = System.getProperty( "user.name" );
438         return username;
439     }
440 
441     public void setSettings( Settings settings )
442     {
443         this.settings = settings;
444     }
445 }