001package org.apache.maven.scm.provider.accurev; 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 022import java.io.File; 023import java.util.Date; 024import java.util.List; 025import java.util.Map; 026import java.util.regex.Matcher; 027import java.util.regex.Pattern; 028 029import org.apache.maven.scm.ScmVersion; 030import org.apache.maven.scm.log.ScmLogger; 031import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost; 032import org.apache.maven.scm.provider.accurev.util.WorkspaceUtils; 033import org.codehaus.plexus.util.StringUtils; 034 035/** 036 * 037 */ 038public class AccuRevScmProviderRepository 039 extends ScmProviderRepositoryWithHost 040{ 041 public static final String DEFAULT_TAG_FORMAT = "%s"; 042 043 private AccuRev accurev; 044 045 private String streamName; 046 047 private String projectPath; 048 049 private String tagFormat = DEFAULT_TAG_FORMAT; 050 051 private ScmLogger logger; 052 053 public AccuRevScmProviderRepository() 054 { 055 super(); 056 // True is a more sensible default (ie for tck tests) 057 // TODO raise jira so tck tests properly handle setPersist 058 setPersistCheckout( true ); 059 060 setShouldUseExportForNonPersistentCheckout( true ); 061 } 062 063 public String getTagFormat() 064 { 065 return tagFormat; 066 } 067 068 public void setTagFormat( String tagFormat ) 069 { 070 if ( tagFormat == null || !tagFormat.contains( "%s" ) ) 071 { 072 throw new IllegalArgumentException( "tagFormat must contain '%s' to be replaced" ); 073 } 074 this.tagFormat = tagFormat; 075 } 076 077 public String getStreamName() 078 { 079 return streamName; 080 } 081 082 public void setStreamName( String streamName ) 083 { 084 this.streamName = streamName; 085 } 086 087 public String getProjectPath() 088 { 089 return projectPath; 090 } 091 092 public void setProjectPath( String projectPath ) 093 { 094 this.projectPath = projectPath; 095 setCheckoutRelativePath( projectPath ); 096 } 097 098 public AccuRev getAccuRev() 099 { 100 return this.accurev; 101 } 102 103 public void setAccuRev( AccuRev accurev ) 104 { 105 this.accurev = accurev; 106 } 107 108 /** 109 * @param info 110 * @return true if info indicates a root of the workspace. 111 */ 112 public boolean isWorkSpaceRoot( AccuRevInfo info ) 113 { 114 String p = getProjectPath(); 115 return ( p != null && WorkspaceUtils.isSameFile( info.getBasedir(), new File( info.getTop(), p ) ) ) 116 || isWorkSpaceTop( info ); 117 } 118 119 public boolean isWorkSpaceTop( AccuRevInfo info ) 120 { 121 return info.isWorkSpaceTop(); 122 123 } 124 125 String tagToStream( String tagName ) 126 { 127 return String.format( getTagFormat(), tagName ); 128 } 129 130 String streamToTag( String streamName ) 131 { 132 tagFormat = getTagFormat(); 133 // TODO - strictly we should quote either side of the %s 134 String tagPatternString = tagToStream( "(.*)" ); 135 Pattern tagPattern = Pattern.compile( tagPatternString ); 136 137 Matcher tagMatcher = tagPattern.matcher( streamName ); 138 if ( tagMatcher.matches() ) 139 { 140 return tagMatcher.group( 1 ); 141 } 142 else 143 { 144 return streamName; 145 } 146 147 } 148 149 public void setLogger( ScmLogger logger ) 150 { 151 this.logger = logger; 152 } 153 154 // TODO raise JIRA to pull up these methods to ScmProviderRepository 155 156 private String checkoutRelativePath; 157 158 private boolean shouldUseExportForNonPersistentCheckout = true; 159 160 /** 161 * The relative path of the directory of the checked out project in comparison to the checkout directory, or an 162 * empty String in case the checkout directory equals the project directory. 163 * <p/> 164 * With most SCMs, this is just an empty String, meaning that the checkout directory equals the project directory. 165 * But there are cases (e.g. ClearCase) where within the checkout directory, the directory structure of the SCM 166 * system is repeated. E.g. if you check out the project "my/project" to "/some/dir", the project sources are 167 * actually checked out to "some/dir/my/project". In this example, relativePathProjectDirectory would contain 168 * "my/project". 169 */ 170 public String getCheckoutRelativePath() 171 { 172 if ( this.checkoutRelativePath == null ) 173 { 174 return ""; 175 } 176 return this.checkoutRelativePath; 177 } 178 179 public void setCheckoutRelativePath( String checkoutRelativePath ) 180 { 181 this.checkoutRelativePath = checkoutRelativePath; 182 } 183 184 /** 185 * Relative project path for export 186 * 187 * @return default same as {@link #getCheckoutRelativePath()} 188 */ 189 public String getExportRelativePath() 190 { 191 return getCheckoutRelativePath(); 192 } 193 194 /** 195 * When checkout is not expected to be refreshed or committed, should export be used instead? Perforce, Clearcase 196 * and AccuRev store their meta-data about file status within the server rather than files in the source tree. This 197 * makes checkouts within checkouts (eg release:perform) difficult. Typically there is a way to do a lightweight 198 * export instead which can be implemented as the "export" command. This is a hint to downstream applications that 199 * "export" is available and should be used in preference to "checkout" in cases where "update" and "commit" are not 200 * intended to be used. (ie release:perform) 201 * 202 * @return false by default 203 */ 204 public boolean shouldUseExportForNonPersistentCheckout() 205 { 206 return this.shouldUseExportForNonPersistentCheckout; 207 } 208 209 public void setShouldUseExportForNonPersistentCheckout( boolean shouldUseExportForNonPersistentCheckout ) 210 { 211 this.shouldUseExportForNonPersistentCheckout = shouldUseExportForNonPersistentCheckout; 212 } 213 214 public String getDepotRelativeProjectPath() 215 { 216 return "/./" + ( projectPath == null ? "" : projectPath ); 217 } 218 219 public AccuRevVersion getAccuRevVersion( ScmVersion scmVersion ) 220 { 221 222 String tran = null; 223 String basisStream = null; 224 225 if ( scmVersion == null ) 226 { 227 basisStream = getStreamName(); 228 } 229 else 230 { 231 String name = StringUtils.clean( scmVersion.getName() ); 232 233 String[] versionComponents = name.split( "[/\\\\]", 2 ); 234 basisStream = versionComponents[0]; 235 if ( basisStream.length() == 0 ) 236 { 237 // Use the default stream from the URL 238 basisStream = getStreamName(); 239 } 240 else 241 { 242 // name is a tag name - convert to a stream. 243 basisStream = tagToStream( basisStream ); 244 } 245 246 if ( versionComponents.length == 2 && versionComponents[1].length() > 0 ) 247 { 248 tran = versionComponents[1]; 249 } 250 } 251 252 return new AccuRevVersion( basisStream, tran ); 253 } 254 255 public String getSnapshotName( String tagName ) 256 { 257 return tagToStream( tagName ); 258 } 259 260 public String getRevision( String streamName, Date date ) 261 { 262 return getRevision( streamName, AccuRev.ACCUREV_TIME_SPEC.format( date == null ? new Date() : date ) ); 263 } 264 265 public String getRevision( String stream, long fromTranId ) 266 { 267 return getRevision( stream, Long.toString( fromTranId ) ); 268 } 269 270 public String getRevision( String streamName, String transaction ) 271 { 272 return streamToTag( streamName ) + "/" + transaction; 273 } 274 275 public String getWorkSpaceRevision( String workspace ) 276 throws AccuRevException 277 { 278 return getRevision( workspace, Long.toString( getCurrentTransactionId( workspace ) ) ); 279 } 280 281 public Transaction getDepotTransaction( String stream, String tranSpec ) 282 throws AccuRevException 283 { 284 285 if ( tranSpec == null ) 286 { 287 tranSpec = "now"; 288 } 289 290 List<Transaction> transactions = getAccuRev().history( stream, tranSpec, null, 1, true, true ); 291 292 if ( transactions == null || transactions.isEmpty() ) 293 { 294 logger.warn( "Unable to find transaction for tranSpec=" + tranSpec ); 295 return null; 296 } 297 else 298 { 299 return transactions.get( 0 ); 300 } 301 302 } 303 304 public String getDepotTransactionId( String stream, String tranSpec ) 305 throws AccuRevException 306 { 307 Transaction t = getDepotTransaction( stream, tranSpec ); 308 309 return t == null ? tranSpec : Long.toString( t.getTranId() ); 310 } 311 312 private long getCurrentTransactionId( String workSpaceName ) 313 throws AccuRevException 314 { 315 // AccuRev does not have a way to get at this workspace info by name. 316 // So we have to do it the hard way... 317 318 AccuRev accuRev = getAccuRev(); 319 320 Map<String, WorkSpace> workSpaces = accuRev.showWorkSpaces(); 321 322 WorkSpace workspace = workSpaces.get( workSpaceName ); 323 324 if ( workspace == null ) 325 { 326 // Must be a reftree 327 workSpaces = accuRev.showRefTrees(); 328 workspace = workSpaces.get( workSpaceName ); 329 } 330 331 if ( workspace == null ) 332 { 333 throw new AccuRevException( "Can't find workspace " + workSpaceName ); 334 } 335 return workspace.getTransactionId(); 336 } 337 338 public String toString() 339 { 340 StringBuilder buff = new StringBuilder( "AccuRevScmProviderRepository" ); 341 buff.append( " user=" ); 342 buff.append( getUser() ); 343 buff.append( " pass=" ); 344 buff.append( getPassword() == null ? "null" : StringUtils.repeat( "*", getPassword().length() ) ); 345 buff.append( " host=" ); 346 buff.append( getHost() ); 347 buff.append( " port=" ); 348 buff.append( getPort() ); 349 buff.append( " stream=" ); 350 buff.append( getStreamName() ); 351 buff.append( " projectPath=" ); 352 buff.append( getProjectPath() ); 353 354 return buff.toString(); 355 } 356 357 public static String formatTimeSpec( Date when ) 358 { 359 360 if ( when == null ) 361 { 362 return "now"; 363 } 364 365 return AccuRev.ACCUREV_TIME_SPEC.format( when ); 366 367 } 368 369}