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  /**
36   * 
37   */
38  public class AccuRevScmProviderRepository
39      extends ScmProviderRepositoryWithHost
40  {
41      public static final String DEFAULT_TAG_FORMAT = "%s";
42  
43      private AccuRev accurev;
44  
45      private String streamName;
46  
47      private String projectPath;
48  
49      private String tagFormat = DEFAULT_TAG_FORMAT;
50  
51      private ScmLogger logger;
52  
53      public AccuRevScmProviderRepository()
54      {
55          super();
56          // True is a more sensible default (ie for tck tests)
57          // TODO raise jira so tck tests properly handle setPersist
58          setPersistCheckout( true );
59  
60          setShouldUseExportForNonPersistentCheckout( true );
61      }
62  
63      public String getTagFormat()
64      {
65          return tagFormat;
66      }
67  
68      public void setTagFormat( String tagFormat )
69      {
70          if ( tagFormat == null || !tagFormat.contains( "%s" ) )
71          {
72              throw new IllegalArgumentException( "tagFormat must contain '%s' to be replaced" );
73          }
74          this.tagFormat = tagFormat;
75      }
76  
77      public String getStreamName()
78      {
79          return streamName;
80      }
81  
82      public void setStreamName( String streamName )
83      {
84          this.streamName = streamName;
85      }
86  
87      public String getProjectPath()
88      {
89          return projectPath;
90      }
91  
92      public void setProjectPath( String projectPath )
93      {
94          this.projectPath = projectPath;
95          setCheckoutRelativePath( projectPath );
96      }
97  
98      public AccuRev getAccuRev()
99      {
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 }