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