001 package org.apache.maven.scm.provider.clearcase.command.checkout;
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 java.io.File;
023 import java.io.FileWriter;
024 import java.io.IOException;
025 import java.net.InetAddress;
026 import java.net.UnknownHostException;
027
028 import org.apache.maven.scm.ScmException;
029 import org.apache.maven.scm.ScmFileSet;
030 import org.apache.maven.scm.ScmVersion;
031 import org.apache.maven.scm.command.checkout.AbstractCheckOutCommand;
032 import org.apache.maven.scm.command.checkout.CheckOutScmResult;
033 import org.apache.maven.scm.provider.ScmProviderRepository;
034 import org.apache.maven.scm.provider.clearcase.command.ClearCaseCommand;
035 import org.apache.maven.scm.provider.clearcase.repository.ClearCaseScmProviderRepository;
036 import org.apache.maven.scm.providers.clearcase.settings.Settings;
037 import org.codehaus.plexus.util.FileUtils;
038 import org.codehaus.plexus.util.StringUtils;
039 import org.codehaus.plexus.util.cli.CommandLineException;
040 import org.codehaus.plexus.util.cli.CommandLineUtils;
041 import org.codehaus.plexus.util.cli.Commandline;
042
043 /**
044 * @author <a href="mailto:wim.deblauwe@gmail.com">Wim Deblauwe</a>
045 * @author <a href="mailto:frederic.mura@laposte.net">Frederic Mura</a>
046 *
047 */
048 public class ClearCaseCheckOutCommand
049 extends AbstractCheckOutCommand
050 implements ClearCaseCommand
051 {
052 private Settings settings = null;
053
054 // ----------------------------------------------------------------------
055 // AbstractCheckOutCommand Implementation
056 // ----------------------------------------------------------------------
057
058 /** {@inheritDoc} */
059 protected CheckOutScmResult executeCheckOutCommand( ScmProviderRepository repository, ScmFileSet fileSet,
060 ScmVersion version, boolean recursive )
061 throws ScmException
062 {
063 if ( getLogger().isDebugEnabled() )
064 {
065 getLogger().debug( "executing checkout command..." );
066 }
067 ClearCaseScmProviderRepository repo = (ClearCaseScmProviderRepository) repository;
068 File workingDirectory = fileSet.getBasedir();
069
070 if ( version != null && getLogger().isDebugEnabled() )
071 {
072 getLogger().debug( version.getType() + ": " + version.getName() );
073 }
074
075 if ( getLogger().isDebugEnabled() )
076 {
077 getLogger().debug( "Running with CLEARCASE " + settings.getClearcaseType() );
078 }
079
080 ClearCaseCheckOutConsumer consumer = new ClearCaseCheckOutConsumer( getLogger() );
081
082 CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
083
084 int exitCode;
085
086 Commandline cl;
087 String projectDirectory = "";
088
089 try
090 {
091 // Since clearcase only wants to checkout to a non-existent directory, first delete the working dir
092 // if it already exists
093 FileUtils.deleteDirectory( workingDirectory );
094 // First create the view
095 String viewName = getUniqueViewName( repo, workingDirectory.getAbsolutePath() );
096 String streamIdentifier = getStreamIdentifier( repo.getStreamName(), repo.getVobName() );
097 cl = createCreateViewCommandLine( workingDirectory, viewName, streamIdentifier );
098 if ( getLogger().isInfoEnabled() )
099 {
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 }