View Javadoc
1   package org.apache.maven.scm.provider.accurev;
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.util.Date;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.regex.Matcher;
27  import java.util.regex.Pattern;
28  
29  import org.apache.maven.scm.ScmVersion;
30  import org.apache.maven.scm.log.ScmLogger;
31  import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
32  import org.apache.maven.scm.provider.accurev.util.WorkspaceUtils;
33  import org.codehaus.plexus.util.StringUtils;
34  
35  public class AccuRevScmProviderRepository
36      extends ScmProviderRepositoryWithHost
37  {
38      public static final String DEFAULT_TAG_FORMAT = "%s";
39  
40      private AccuRev accurev;
41  
42      private String streamName;
43  
44      private String projectPath;
45  
46      private String tagFormat = DEFAULT_TAG_FORMAT;
47  
48      private ScmLogger logger;
49  
50      public AccuRevScmProviderRepository()
51      {
52          super();
53          // True is a more sensible default (ie for tck tests)
54          // TODO raise jira so tck tests properly handle setPersist
55          setPersistCheckout( true );
56  
57          setShouldUseExportForNonPersistentCheckout( true );
58      }
59  
60      public String getTagFormat()
61      {
62          return tagFormat;
63      }
64  
65      public void setTagFormat( String tagFormat )
66      {
67          if ( tagFormat == null || !tagFormat.contains( "%s" ) )
68          {
69              throw new IllegalArgumentException( "tagFormat must contain '%s' to be replaced" );
70          }
71          this.tagFormat = tagFormat;
72      }
73  
74      public String getStreamName()
75      {
76          return streamName;
77      }
78  
79      public void setStreamName( String streamName )
80      {
81          this.streamName = streamName;
82      }
83  
84      public String getProjectPath()
85      {
86          return projectPath;
87      }
88  
89      public void setProjectPath( String projectPath )
90      {
91          this.projectPath = projectPath;
92          setCheckoutRelativePath( projectPath );
93      }
94  
95      public AccuRev getAccuRev()
96      {
97          return this.accurev;
98      }
99  
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 }