001 package org.apache.maven.scm.provider.git.repository; 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 org.apache.maven.scm.ScmException; 023 import org.apache.maven.scm.provider.ScmProviderRepository; 024 import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost; 025 026 import java.util.regex.Matcher; 027 import java.util.regex.Pattern; 028 029 /** 030 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a> 031 * @author <a href="mailto:struberg@apache.org">Mark Struberg</a> 032 * @version $Id: GitScmProviderRepository.java 1306864 2012-03-29 13:43:18Z olamy $ 033 */ 034 public class GitScmProviderRepository 035 extends ScmProviderRepositoryWithHost 036 { 037 038 /** 039 * sequence used to delimit the fetch URL 040 */ 041 public static final String URL_DELIMITER_FETCH = "[fetch=]"; 042 043 /** 044 * sequence used to delimit the push URL 045 */ 046 public static final String URL_DELIMITER_PUSH = "[push=]"; 047 048 /** 049 * this trails every protocol 050 */ 051 public static final String PROTOCOL_SEPARATOR = "://"; 052 053 /** 054 * use local file as transport 055 */ 056 public static final String PROTOCOL_FILE = "file"; 057 058 /** 059 * use gits internal protocol 060 */ 061 public static final String PROTOCOL_GIT = "git"; 062 063 /** 064 * use secure shell protocol 065 */ 066 public static final String PROTOCOL_SSH = "ssh"; 067 068 /** 069 * use the standard port 80 http protocol 070 */ 071 public static final String PROTOCOL_HTTP = "http"; 072 073 /** 074 * use the standard port 443 https protocol 075 */ 076 public static final String PROTOCOL_HTTPS = "https"; 077 078 /** 079 * use rsync for retrieving the data 080 * TODO implement! 081 */ 082 public static final String PROTOCOL_RSYNC = "rsync"; 083 084 private static final Pattern HOST_AND_PORT_EXTRACTOR = 085 Pattern.compile( "([^:/\\\\~]*)(?::(\\d*))?(?:([:/\\\\~])(.*))?" ); 086 087 /** 088 * No special protocol specified. Git will either use git:// 089 * or ssh:// depending on whether we work locally or over the network 090 */ 091 public static final String PROTOCOL_NONE = ""; 092 093 /** 094 * this may either 'git' or 'jgit' depending on the underlying implementation being used 095 */ 096 private String provider; 097 098 /** 099 * the URL used to fetch from the upstream repository 100 */ 101 private RepositoryUrl fetchInfo; 102 103 /** 104 * the URL used to push to the upstream repository 105 */ 106 private RepositoryUrl pushInfo; 107 108 public GitScmProviderRepository( String url ) 109 throws ScmException 110 { 111 if ( url == null ) 112 { 113 throw new ScmException( "url must not be null" ); 114 } 115 116 if ( url.startsWith( URL_DELIMITER_FETCH ) ) 117 { 118 String fetch = url.substring( URL_DELIMITER_FETCH.length() ); 119 120 int indexPushDelimiter = fetch.indexOf( URL_DELIMITER_PUSH ); 121 if ( indexPushDelimiter >= 0 ) 122 { 123 String push = fetch.substring( indexPushDelimiter + URL_DELIMITER_PUSH.length() ); 124 pushInfo = parseUrl( push ); 125 126 fetch = fetch.substring( 0, indexPushDelimiter ); 127 } 128 129 fetchInfo = parseUrl( fetch ); 130 131 if ( pushInfo == null ) 132 { 133 pushInfo = fetchInfo; 134 } 135 } 136 else if ( url.startsWith( URL_DELIMITER_PUSH ) ) 137 { 138 String push = url.substring( URL_DELIMITER_PUSH.length() ); 139 140 int indexFetchDelimiter = push.indexOf( URL_DELIMITER_FETCH ); 141 if ( indexFetchDelimiter >= 0 ) 142 { 143 String fetch = push.substring( indexFetchDelimiter + URL_DELIMITER_FETCH.length() ); 144 fetchInfo = parseUrl( fetch ); 145 146 push = push.substring( 0, indexFetchDelimiter ); 147 } 148 149 pushInfo = parseUrl( push ); 150 151 if ( fetchInfo == null ) 152 { 153 fetchInfo = pushInfo; 154 } 155 } 156 else 157 { 158 fetchInfo = pushInfo = parseUrl( url ); 159 } 160 161 // set the default values for backward compatibility from the push url 162 // because it's more likely that the push URL contains 'better' credentials 163 setUser( pushInfo.getUserName() ); 164 setPassword( pushInfo.getPassword() ); 165 setHost( pushInfo.getHost() ); 166 if ( pushInfo.getPort() != null && pushInfo.getPort().length() > 0 ) 167 { 168 setPort( Integer.parseInt( pushInfo.getPort() ) ); 169 } 170 } 171 172 public GitScmProviderRepository( String url, String user, String password ) 173 throws ScmException 174 { 175 this( url ); 176 177 setUser( user ); 178 179 setPassword( password ); 180 } 181 182 /** 183 * @return either 'git' or 'jgit' depending on the underlying implementation being used 184 */ 185 public String getProvider() 186 { 187 return provider; 188 } 189 190 public RepositoryUrl getFetchInfo() 191 { 192 return fetchInfo; 193 } 194 195 public RepositoryUrl getPushInfo() 196 { 197 return pushInfo; 198 } 199 200 201 /** 202 * @return the URL used to fetch from the upstream repository 203 */ 204 public String getFetchUrl() 205 { 206 return getUrl( fetchInfo ); 207 } 208 209 /** 210 * @return the URL used to push to the upstream repository 211 */ 212 public String getPushUrl() 213 { 214 return getUrl( pushInfo ); 215 } 216 217 218 /** 219 * Parse the given url string and store all the extracted 220 * information in a {@code RepositoryUrl} 221 * 222 * @param url to parse 223 * @return filled with the information from the given URL 224 * @throws ScmException 225 */ 226 private RepositoryUrl parseUrl( String url ) 227 throws ScmException 228 { 229 RepositoryUrl repoUrl = new RepositoryUrl(); 230 231 url = parseProtocol( repoUrl, url ); 232 url = parseUserInfo( repoUrl, url ); 233 url = parseHostAndPort( repoUrl, url ); 234 // the rest of the url must be the path to the repository on the server 235 repoUrl.setPath( url ); 236 return repoUrl; 237 } 238 239 240 /** 241 * @param repoUrl 242 * @return 243 */ 244 private String getUrl( RepositoryUrl repoUrl ) 245 { 246 StringBuilder urlSb = new StringBuilder( repoUrl.getProtocol() ); 247 boolean urlSupportsUserInformation = false; 248 249 if ( PROTOCOL_SSH.equals( repoUrl.getProtocol() ) || 250 PROTOCOL_RSYNC.equals( repoUrl.getProtocol() ) || 251 PROTOCOL_GIT.equals( repoUrl.getProtocol() ) || 252 PROTOCOL_HTTP.equals( repoUrl.getProtocol() ) || 253 PROTOCOL_HTTPS.equals( repoUrl.getProtocol() ) || 254 PROTOCOL_NONE.equals( repoUrl.getProtocol() ) ) 255 { 256 urlSupportsUserInformation = true; 257 } 258 259 if ( repoUrl.getProtocol() != null && repoUrl.getProtocol().length() > 0 ) 260 { 261 urlSb.append( "://" ); 262 } 263 264 // add user information if given and allowed for the protocol 265 if ( urlSupportsUserInformation ) 266 { 267 String userName = repoUrl.getUserName(); 268 // if specified on the commandline or other configuration, we take this. 269 if ( getUser() != null && getUser().length() > 0 ) 270 { 271 userName = getUser(); 272 } 273 274 String password = repoUrl.getPassword(); 275 if ( getPassword() != null && getPassword().length() > 0 ) 276 { 277 password = getPassword(); 278 } 279 280 if ( userName != null && userName.length() > 0 ) 281 { 282 urlSb.append( userName ); 283 284 if ( password != null && password.length() > 0 ) 285 { 286 urlSb.append( ':' ).append( password ); 287 } 288 289 urlSb.append( '@' ); 290 } 291 } 292 293 // add host and port information 294 urlSb.append( repoUrl.getHost() ); 295 if ( repoUrl.getPort() != null && repoUrl.getPort().length() > 0 ) 296 { 297 urlSb.append( ':' ).append( repoUrl.getPort() ); 298 } 299 300 // finaly we add the path to the repo on the host 301 urlSb.append( repoUrl.getPath() ); 302 303 return urlSb.toString(); 304 } 305 306 /** 307 * Parse the protocol from the given url and fill it into the given RepositoryUrl. 308 * 309 * @param repoUrl 310 * @param url 311 * @return the given url with the protocol parts removed 312 */ 313 private String parseProtocol( RepositoryUrl repoUrl, String url ) 314 throws ScmException 315 { 316 // extract the protocol 317 if ( url.startsWith( PROTOCOL_FILE + PROTOCOL_SEPARATOR ) ) 318 { 319 repoUrl.setProtocol( PROTOCOL_FILE ); 320 } 321 else if ( url.startsWith( PROTOCOL_HTTPS + PROTOCOL_SEPARATOR ) ) 322 { 323 repoUrl.setProtocol( PROTOCOL_HTTPS ); 324 } 325 else if ( url.startsWith( PROTOCOL_HTTP + PROTOCOL_SEPARATOR ) ) 326 { 327 repoUrl.setProtocol( PROTOCOL_HTTP ); 328 } 329 else if ( url.startsWith( PROTOCOL_SSH + PROTOCOL_SEPARATOR ) ) 330 { 331 repoUrl.setProtocol( PROTOCOL_SSH ); 332 } 333 else if ( url.startsWith( PROTOCOL_GIT + PROTOCOL_SEPARATOR ) ) 334 { 335 repoUrl.setProtocol( PROTOCOL_GIT ); 336 } 337 else if ( url.startsWith( PROTOCOL_RSYNC + PROTOCOL_SEPARATOR ) ) 338 { 339 repoUrl.setProtocol( PROTOCOL_RSYNC ); 340 } 341 else 342 { 343 // when no protocol is specified git will pick either ssh:// or git:// 344 // depending on whether we work locally or over the network 345 repoUrl.setProtocol( PROTOCOL_NONE ); 346 return url; 347 } 348 349 url = url.substring( repoUrl.getProtocol().length() + 3 ); 350 351 return url; 352 } 353 354 /** 355 * Parse the user information from the given url and fill 356 * user name and password into the given RepositoryUrl. 357 * 358 * @param repoUrl 359 * @param url 360 * @return the given url with the user parts removed 361 */ 362 private String parseUserInfo( RepositoryUrl repoUrl, String url ) 363 throws ScmException 364 { 365 // extract user information 366 int indexAt = url.indexOf( '@' ); 367 if ( indexAt >= 0 ) 368 { 369 String userInfo = url.substring( 0, indexAt ); 370 int indexPwdSep = userInfo.indexOf( ':' ); 371 if ( indexPwdSep < 0 ) 372 { 373 repoUrl.setUserName( userInfo ); 374 } 375 else 376 { 377 repoUrl.setUserName( userInfo.substring( 0, indexPwdSep ) ); 378 repoUrl.setPassword( userInfo.substring( indexPwdSep + 1 ) ); 379 } 380 381 url = url.substring( indexAt + 1 ); 382 } 383 return url; 384 } 385 386 /** 387 * Parse server and port from the given url and fill it into the 388 * given RepositoryUrl. 389 * 390 * @param repoUrl 391 * @param url 392 * @return the given url with the server parts removed 393 * @throws ScmException 394 */ 395 private String parseHostAndPort( RepositoryUrl repoUrl, String url ) 396 throws ScmException 397 { 398 399 repoUrl.setPort( "" ); 400 repoUrl.setHost( "" ); 401 402 if ( PROTOCOL_FILE.equals( repoUrl.getProtocol() ) ) 403 { 404 // a file:// URL doesn't need any further parsing as it cannot contain a port, etc 405 return url; 406 } 407 else 408 { 409 410 Matcher hostAndPortMatcher = HOST_AND_PORT_EXTRACTOR.matcher( url ); 411 if ( hostAndPortMatcher.matches() ) 412 { 413 if ( hostAndPortMatcher.groupCount() > 1 && hostAndPortMatcher.group( 1 ) != null ) 414 { 415 repoUrl.setHost( hostAndPortMatcher.group( 1 ) ); 416 } 417 if ( hostAndPortMatcher.groupCount() > 2 && hostAndPortMatcher.group( 2 ) != null ) 418 { 419 repoUrl.setPort( hostAndPortMatcher.group( 2 ) ); 420 } 421 422 StringBuilder computedUrl = new StringBuilder(); 423 if ( hostAndPortMatcher.group( hostAndPortMatcher.groupCount() - 1 ) != null ) 424 { 425 computedUrl.append( hostAndPortMatcher.group( hostAndPortMatcher.groupCount() - 1 ) ); 426 } 427 if ( hostAndPortMatcher.group( hostAndPortMatcher.groupCount() ) != null ) 428 { 429 computedUrl.append( hostAndPortMatcher.group( hostAndPortMatcher.groupCount() ) ); 430 } 431 return computedUrl.toString(); 432 } 433 else 434 { 435 // Pattern doesn't match, let's return the original url 436 return url; 437 } 438 } 439 } 440 441 442 /** 443 * {@inheritDoc} 444 */ 445 public String getRelativePath( ScmProviderRepository ancestor ) 446 { 447 if ( ancestor instanceof GitScmProviderRepository ) 448 { 449 GitScmProviderRepository gitAncestor = (GitScmProviderRepository) ancestor; 450 451 //X TODO review! 452 String url = getFetchUrl(); 453 String path = url.replaceFirst( gitAncestor.getFetchUrl() + "/", "" ); 454 455 if ( !path.equals( url ) ) 456 { 457 return path; 458 } 459 } 460 return null; 461 } 462 463 /** 464 * {@inheritDoc} 465 */ 466 public String toString() 467 { 468 // yes we really like to check if those are the exact same instance! 469 if ( fetchInfo == pushInfo ) 470 { 471 return getUrl( fetchInfo ); 472 } 473 return URL_DELIMITER_FETCH + getUrl( fetchInfo ) + 474 URL_DELIMITER_PUSH + getUrl( pushInfo ); 475 } 476 477 }