View Javadoc

1   package org.apache.maven.plugin.jira;
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 org.apache.commons.httpclient.Credentials;
23  import org.apache.commons.httpclient.Header;
24  import org.apache.commons.httpclient.HostConfiguration;
25  import org.apache.commons.httpclient.HttpClient;
26  import org.apache.commons.httpclient.HttpException;
27  import org.apache.commons.httpclient.HttpState;
28  import org.apache.commons.httpclient.HttpStatus;
29  import org.apache.commons.httpclient.StatusLine;
30  import org.apache.commons.httpclient.UsernamePasswordCredentials;
31  import org.apache.commons.httpclient.auth.AuthScope;
32  import org.apache.commons.httpclient.cookie.CookiePolicy;
33  import org.apache.commons.httpclient.methods.GetMethod;
34  import org.apache.commons.httpclient.params.HttpClientParams;
35  import org.apache.maven.plugin.MojoExecutionException;
36  import org.apache.maven.plugin.issues.Issue;
37  import org.apache.maven.plugin.issues.IssueUtils;
38  import org.apache.maven.plugin.logging.Log;
39  import org.apache.maven.project.MavenProject;
40  import org.apache.maven.settings.Proxy;
41  import org.apache.maven.settings.Settings;
42  import org.apache.maven.wagon.proxy.ProxyInfo;
43  import org.codehaus.plexus.util.IOUtil;
44  import org.codehaus.plexus.util.StringUtils;
45  
46  import java.io.File;
47  import java.io.FileOutputStream;
48  import java.io.IOException;
49  import java.io.InputStream;
50  import java.io.OutputStream;
51  import java.net.MalformedURLException;
52  import java.net.URL;
53  import java.net.URLEncoder;
54  import java.util.Collections;
55  import java.util.List;
56  import java.util.Map;
57  
58  /**
59   * Gets relevant issues in RSS from a given JIRA installation.
60   * <p/>
61   * Based on version 1.1.2 and patch by Dr. Spock (MPJIRA-8).
62   *
63   * @author mfranken@xebia.com
64   * @author jruiz@exist.com
65   * @version $Id: AbstractJiraDownloader.java 1379895 2012-09-01 23:27:41Z dennisl $
66   */
67  public abstract class AbstractJiraDownloader
68  {
69      private static final String UTF_8 = "UTF-8";
70  
71      /** Log for debug output. */
72      protected Log log;
73      /** Output file for xml document. */
74      private File output;
75      /** The maximum number of entries to show. */
76      private int nbEntriesMax;
77      /** The filter to apply to query to JIRA. */
78      private String filter;
79      /** Ids of fix versions to show, as comma separated string. */
80      private String fixVersionIds;
81      /** Ids of status to show, as comma separated string. */
82      private String statusIds;
83      /** Ids of resolution to show, as comma separated string. */
84      private String resolutionIds;
85      /** Ids of priority to show, as comma separated string. */
86      private String priorityIds;
87      /** The component to show. */
88      private String component;
89      /** Ids of types to show, as comma separated string. */
90      private String typeIds;
91      /** Column names to sort by, as comma separated string. */
92      private String sortColumnNames;
93      /** The username to log into JIRA. */
94      private String jiraUser;
95      /** The password to log into JIRA. */
96      private String jiraPassword;
97      /** The username to log into webserver. */
98      private String webUser;
99      /** The password to log into webserver. */
100     private String webPassword;
101     /** The maven project. */
102     private MavenProject project;
103     /** The maven settings. */
104     private Settings settings;
105     /** Use JQL, JIRA query language, instead of URL parameter based queries */
106     private boolean useJql;
107     /** Filter the JIRA query based on the current version */
108     private boolean onlyCurrentVersion;
109     /** The versionPrefix to apply to the POM version */
110     private String versionPrefix;
111     /** The pattern used to parse dates from the JIRA xml file. */
112     protected String jiraDatePattern;
113 
114     /**
115      * Execute the query on the JIRA server.
116      *
117      * @throws Exception on error
118      */
119     public void doExecute()
120         throws Exception
121     {
122         try
123         {
124             HttpClient client = new HttpClient();
125 
126             // MCHANGES-89 Allow circular redirects
127             HttpClientParams clientParams = client.getParams();
128             clientParams.setBooleanParameter( HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true );
129             clientParams.setCookiePolicy( CookiePolicy.BROWSER_COMPATIBILITY ); //MCHANGES-237
130 
131             HttpState state = new HttpState();
132 
133             HostConfiguration hc = new HostConfiguration();
134 
135             client.setHostConfiguration( hc );
136 
137             client.setState( state );
138 
139             String fullUrl = null;
140 
141             if ( useJql )
142             {
143                 fullUrl = getJqlQueryURL();
144             }
145             else
146             {
147                 fullUrl = getParameterBasedQueryURL( client );
148             }
149             String baseUrl = JiraHelper.getBaseUrl( fullUrl );
150 
151             getLog().debug( "JIRA lives at: " + baseUrl );
152             determineProxy( baseUrl, client );
153 
154             prepareBasicAuthentication( client );
155 
156             boolean jiraAuthenticationSuccessful = false;
157             if ( isJiraAuthenticationConfigured() )
158             {
159                 jiraAuthenticationSuccessful = doJiraAuthentication( client, baseUrl );
160             }
161 
162             if ( ( isJiraAuthenticationConfigured() && jiraAuthenticationSuccessful )
163                 || !isJiraAuthenticationConfigured() )
164             {
165                 if ( log.isDebugEnabled() )
166                 {
167                     log.debug( "download jira issues from url " + fullUrl );
168                 }
169 
170                 // execute the GET
171                 download( client, fullUrl );
172             }
173         }
174         catch ( Exception e )
175         {
176             if ( project.getIssueManagement() != null )
177             {
178                 getLog().error( "Error accessing " + project.getIssueManagement().getUrl(), e );
179             }
180             else
181             {
182                 getLog().error( "Error accessing mock project issues", e );
183             }
184         }
185     }
186 
187     private String getJqlQueryURL()
188     {
189         // JQL is based on project names instead of project ID's
190         Map<String, String> urlMap = JiraHelper.getJiraUrlAndProjectName( project.getIssueManagement().getUrl() );
191         String jiraUrl = urlMap.get( "url" );
192         String jiraProject = urlMap.get( "project" );
193 
194         if ( jiraProject == null )
195         {
196             throw new RuntimeException( "The issue management URL in the POM does not include a JIRA project name" );
197         }
198         else
199         {
200             // create the URL for getting the proper issues from JIRA
201             String jqlQuery = new JqlQueryBuilder( log )
202                     .project( jiraProject )
203                     .fixVersion( getFixFor() )
204                     .fixVersionIds( fixVersionIds )
205                     .statusIds( statusIds )
206                     .priorityIds( priorityIds )
207                     .resolutionIds( resolutionIds )
208                     .components( component )
209                     .typeIds( typeIds )
210                     .sortColumnNames( sortColumnNames )
211                     .build();
212 
213             String url = new UrlBuilder( jiraUrl, "sr/jira.issueviews:searchrequest-xml/temp/SearchRequest.xml" )
214                     .addParameter( "tempMax", nbEntriesMax )
215                     .addParameter( "reset", "true" )
216                     .addParameter( "jqlQuery", jqlQuery )
217                     .build();
218 
219             return url;
220         }
221     }
222 
223     private String getParameterBasedQueryURL( HttpClient client )
224     {
225         Map<String, String> urlMap = JiraHelper.getJiraUrlAndProjectId( project.getIssueManagement().getUrl() );
226         String jiraUrl = urlMap.get( "url" );
227         String jiraId = urlMap.get( "id" );
228 
229         if ( jiraId == null || jiraId.length() == 0 )
230         {
231             log.debug( "The JIRA URL " + project.getIssueManagement().getUrl()
232                            + " doesn't include a pid, trying to extract it from JIRA." );
233             jiraId = JiraHelper.getPidFromJira( log, project.getIssueManagement().getUrl(), client );
234         }
235 
236         if ( jiraId == null )
237         {
238             throw new RuntimeException( "The issue management URL in the POM does not include a pid,"
239                                             + " and it was not possible to extract it from the page at that URL." );
240         }
241         else
242         {
243             // create the URL for getting the proper issues from JIRA
244             String fullURL = jiraUrl + "/secure/IssueNavigator.jspa?view=rss&pid=" + jiraId;
245 
246             if ( getFixFor() != null )
247             {
248                 fullURL += "&fixfor=" + getFixFor();
249             }
250 
251             String createdFilter = new ParameterQueryBuilder( log )
252                     .fixVersionIds( fixVersionIds )
253                     .statusIds( statusIds )
254                     .priorityIds( priorityIds )
255                     .resolutionIds( resolutionIds )
256                     .components( component )
257                     .typeIds( typeIds )
258                     .sortColumnNames( sortColumnNames )
259                     .filter( filter )
260                     .build();
261 
262             if ( createdFilter.charAt( 0 ) != '&' )
263             {
264                 fullURL += "&";
265             }
266             fullURL += createdFilter;
267 
268             fullURL += ( "&tempMax=" + nbEntriesMax + "&reset=true&decorator=none" );
269 
270             return fullURL;
271         }
272     }
273 
274     /**
275      * Override this method if you need to get issues for a specific Fix For.
276      *
277      * @return A Fix For id or <code>null</code> if you don't have that need
278      */
279     protected String getFixFor()
280     {
281         if ( onlyCurrentVersion && useJql )
282         {
283             // Let JIRA do the filtering of the current version instead of the JIRA mojo.
284             // This way JIRA returns less issues and we do not run into the "nbEntriesMax" limit that easily.
285 
286             String version = ( versionPrefix == null ? "" : versionPrefix ) + project.getVersion();
287 
288             // Remove "-SNAPSHOT" from the end of the version, if it's there
289             if ( version != null && version.endsWith( IssueUtils.SNAPSHOT_SUFFIX ) )
290             {
291                 return version.substring( 0, version.length() - IssueUtils.SNAPSHOT_SUFFIX.length() );
292             }
293             else
294             {
295                 return version;
296             }
297         }
298         else
299         {
300             return null;
301         }
302     }
303 
304     /**
305      * Check and prepare for basic authentication.
306      *
307      * @param client The client to prepare
308      */
309     private void prepareBasicAuthentication( HttpClient client )
310     {
311         if ( ( webUser != null ) && ( webUser.length() > 0 ) )
312         {
313             client.getParams().setAuthenticationPreemptive( true );
314 
315             Credentials defaultcreds = new UsernamePasswordCredentials( webUser, webPassword );
316 
317             getLog().debug( "Using username: " + webUser + " for Basic Authentication." );
318 
319             client.getState().setCredentials( new AuthScope( null, AuthScope.ANY_PORT, null, AuthScope.ANY_SCHEME ),
320                                               defaultcreds );
321         }
322     }
323 
324     /**
325      * Authenticate against JIRA. This method relies on jiraUser and
326      * jiraPassword being set. You can check this by calling
327      * isJiraAuthenticationConfigured().
328      *
329      * @param client    the HttpClient
330      * @param jiraUrl   the JIRA installation
331      * @return <code>true</code> if the authentication was successful, otherwise <code>false</code>
332      */
333     private boolean doJiraAuthentication( HttpClient client, final String jiraUrl )
334     {
335         // log into JIRA if we have to
336         String loginUrl = null;
337 
338         StringBuffer loginLink = new StringBuffer( jiraUrl );
339 
340         loginLink.append( "/login.jsp?os_destination=/secure/" );
341 
342         try
343         {
344             loginLink.append( "&os_username=" ).append( URLEncoder.encode( jiraUser, UTF_8 ) );
345 
346             String password = null;
347             if ( jiraPassword != null )
348             {
349                 password = StringUtils.repeat( "*", jiraPassword.length() );
350             }
351             getLog().debug( "Login URL: " + loginLink + "&os_password=" + password );
352 
353             loginLink.append( "&os_password=" ).append( URLEncoder.encode( jiraPassword, UTF_8 ) );
354 
355             loginUrl = loginLink.toString();
356 
357             // execute the login
358             GetMethod loginGet = new GetMethod( loginUrl );
359 
360             client.executeMethod( loginGet );
361 
362             if ( loginSucceeded( loginGet ) )
363             {
364                 getLog().debug( "Successfully logged in into JIRA." );
365                 return true;
366             }
367             else
368             {
369                 getLog().warn( "Was unable to login into JIRA: wrong username and/or password." );
370             }
371         }
372         catch ( Exception e )
373         {
374             if ( getLog().isDebugEnabled() )
375             {
376                 getLog().error( "Error trying to login into JIRA.", e );
377             }
378             else
379             {
380                 getLog().error( "Error trying to login into JIRA. Cause is: " + e.getLocalizedMessage() );
381             }
382         }
383         return false;
384     }
385 
386     /**
387      * Check to see if we think that JIRA authentication is needed.
388      *
389      * @return <code>true</code> if jiraUser and jiraPassword are set, otherwise <code>false</code>
390      */
391     private boolean isJiraAuthenticationConfigured()
392     {
393         return ( jiraUser != null ) && ( jiraUser.length() > 0 ) && ( jiraPassword != null );
394     }
395 
396     /**
397      * Evaluate if the login attempt to JIRA was successful or not. We can't
398      * use the status code because JIRA returns 200 even if the login fails.
399      *
400      * @param loginGet The method that was executed
401      * @return <code>false</code> if we find an error message in the response body, otherwise <code>true</code>
402      * @todo There must be a nicer way to know whether we were able to login or not
403      */
404     private boolean loginSucceeded( GetMethod loginGet )
405         throws IOException
406     {
407         final String loginFailureResponse = "your username and password are incorrect";
408 
409         return loginGet.getResponseBodyAsString().indexOf( loginFailureResponse ) == -1;
410     }
411 
412     /**
413      * Setup proxy access if we have to.
414      *
415      * @param client  the HttpClient
416      */
417     private void determineProxy( String jiraUrl, HttpClient client )
418     {
419         // see whether there is any proxy defined in maven
420         Proxy proxy = null;
421 
422         String proxyHost = null;
423 
424         int proxyPort = 0;
425 
426         String proxyUser = null;
427 
428         String proxyPass = null;
429 
430         if ( project == null )
431         {
432             getLog().error( "No project set. No proxy info available." );
433 
434             return;
435         }
436 
437         if ( settings != null )
438         {
439             proxy = settings.getActiveProxy();
440         }
441 
442         if ( proxy != null )
443         {
444 
445             ProxyInfo proxyInfo = new ProxyInfo();
446             proxyInfo.setNonProxyHosts( proxy.getNonProxyHosts() );
447 
448             // Get the host out of the JIRA URL
449             URL url = null;
450             try
451             {
452                 url = new URL( jiraUrl );
453             }
454             catch( MalformedURLException e )
455             {
456                 getLog().error( "Invalid JIRA URL: " + jiraUrl + ". " + e.getMessage() );
457             }
458             String jiraHost = null;
459             if ( url != null )
460             {
461                 jiraHost = url.getHost();
462             }
463 
464             // Validation of proxy method copied from org.apache.maven.wagon.proxy.ProxyUtils.
465             // @todo Can use original when maven-changes-plugin requires a more recent version of Maven
466 
467             //if ( ProxyUtils.validateNonProxyHosts( proxyInfo, jiraHost ) )
468             if ( JiraHelper.validateNonProxyHosts( proxyInfo, jiraHost ) )
469             {
470                 return;
471             }
472 
473             proxyHost = settings.getActiveProxy().getHost();
474 
475             proxyPort = settings.getActiveProxy().getPort();
476 
477             proxyUser = settings.getActiveProxy().getUsername();
478 
479             proxyPass = settings.getActiveProxy().getPassword();
480 
481             getLog().debug( proxyPass );
482         }
483 
484         if ( proxyHost != null )
485         {
486             client.getHostConfiguration().setProxy( proxyHost, proxyPort );
487 
488             getLog().debug( "Using proxy: " + proxyHost + " at port " + proxyPort );
489 
490             if ( proxyUser != null )
491             {
492                 getLog().debug( "Using proxy user: " + proxyUser );
493 
494                 client.getState().setProxyCredentials(
495                                                        new AuthScope( null, AuthScope.ANY_PORT, null,
496                                                                       AuthScope.ANY_SCHEME ),
497                                                        new UsernamePasswordCredentials( proxyUser, proxyPass ) );
498             }
499         }
500     }
501 
502     /**
503      * Downloads the given link using the configured HttpClient, possibly following redirects.
504      *
505      * @param cl     the HttpClient
506      * @param link   the URL to JIRA
507      */
508     private void download( final HttpClient cl, final String link )
509     {
510         try
511         {
512             GetMethod gm = new GetMethod( link );
513 
514             getLog().info( "Downloading from JIRA at: " + link );
515 
516             gm.setFollowRedirects( true );
517 
518             cl.executeMethod( gm );
519 
520             StatusLine sl = gm.getStatusLine();
521 
522             if ( sl == null )
523             {
524                 getLog().error( "Unknown error validating link: " + link );
525 
526                 return;
527             }
528 
529             // if we get a redirect, do so
530             if ( gm.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY )
531             {
532                 Header locationHeader = gm.getResponseHeader( "Location" );
533 
534                 if ( locationHeader == null )
535                 {
536                     getLog().warn( "Site sent redirect, but did not set Location header" );
537                 }
538                 else
539                 {
540                     String newLink = locationHeader.getValue();
541 
542                     getLog().debug( "Following redirect to " + newLink );
543 
544                     download( cl, newLink );
545                 }
546             }
547 
548             if ( gm.getStatusCode() == HttpStatus.SC_OK )
549             {
550                 final InputStream responseBodyStream = gm.getResponseBodyAsStream();
551 
552                 if ( !output.getParentFile().exists() )
553                 {
554                     output.getParentFile().mkdirs();
555                 }
556 
557                 // write the response to file
558                 OutputStream out = null;
559                 try
560                 {
561                     out = new FileOutputStream( output );
562                     IOUtil.copy( responseBodyStream, out );
563                 }
564                 finally
565                 {
566                     IOUtil.close( out );
567                     IOUtil.close( responseBodyStream );
568                 }
569 
570                 getLog().debug( "Downloading from JIRA was successful" );
571             }
572             else
573             {
574                 getLog().warn( "Downloading from JIRA failed. Received: [" + gm.getStatusCode() + "]" );
575             }
576         }
577         catch ( HttpException e )
578         {
579             if ( getLog().isDebugEnabled() )
580             {
581                 getLog().error( "Error downloading issues from JIRA:", e );
582             }
583             else
584             {
585                 getLog().error( "Error downloading issues from JIRA url: " + e.getLocalizedMessage() );
586 
587             }
588         }
589         catch ( IOException e )
590         {
591             if ( getLog().isDebugEnabled() )
592             {
593                 getLog().error( "Error downloading issues from JIRA:", e );
594             }
595             else
596             {
597                 getLog().error( "Error downloading issues from JIRA. Cause is " + e.getLocalizedMessage() );
598             }
599         }
600     }
601 
602     public List<Issue> getIssueList()
603         throws MojoExecutionException
604     {
605         if ( output.isFile() )
606         {
607             JiraXML jira = new JiraXML( log, jiraDatePattern );
608             jira.parseXML( output );
609             getLog().info( "The JIRA version is '" + jira.getJiraVersion() + "'" );
610             return jira.getIssueList();
611         }
612         else
613         {
614             getLog().warn( "JIRA file " + output.getPath() + " doesn't exist." );
615             return Collections.emptyList();
616         }
617     }
618 
619     public void setJiraDatePattern( String jiraDatePattern )
620     {
621         this.jiraDatePattern = jiraDatePattern;
622     }
623 
624     /**
625      * Set the output file for the log.
626      *
627      * @param thisOutput the output file
628      */
629     public void setOutput( File thisOutput )
630     {
631         this.output = thisOutput;
632     }
633 
634     public File getOutput()
635     {
636         return this.output;
637     }
638 
639     /**
640      * Sets the project.
641      *
642      * @param thisProject  The project to set
643      */
644     public void setMavenProject( Object thisProject )
645     {
646         this.project = (MavenProject) thisProject;
647     }
648 
649     /**
650      * Sets the maximum number of Issues to show.
651      *
652      * @param nbEntries  The maximum number of Issues
653      */
654     public void setNbEntries( final int nbEntries )
655     {
656         nbEntriesMax = nbEntries;
657     }
658 
659     /**
660      * Sets the statusIds.
661      *
662      * @param thisStatusIds   The id(s) of the status to show, as comma separated string
663      */
664     public void setStatusIds( String thisStatusIds )
665     {
666         statusIds = thisStatusIds;
667     }
668 
669     /**
670      * Sets the priorityIds.
671      *
672      * @param thisPriorityIds  The id(s) of the priority to show, as comma separated string
673      */
674     public void setPriorityIds( String thisPriorityIds )
675     {
676         priorityIds = thisPriorityIds;
677     }
678 
679     /**
680      * Sets the resolutionIds.
681      *
682      * @param thisResolutionIds  The id(s) of the resolution to show, as comma separated string
683      */
684     public void setResolutionIds( String thisResolutionIds )
685     {
686         resolutionIds = thisResolutionIds;
687     }
688 
689     /**
690      * Sets the sort column names.
691      *
692      * @param thisSortColumnNames The column names to sort by
693      */
694     public void setSortColumnNames( String thisSortColumnNames )
695     {
696         sortColumnNames = thisSortColumnNames;
697     }
698 
699     /**
700      * Sets the password for authentication against the webserver.
701      *
702      * @param thisWebPassword  The password of the webserver
703      */
704     public void setWebPassword( String thisWebPassword )
705     {
706         this.webPassword = thisWebPassword;
707     }
708 
709     /**
710      * Sets the username for authentication against the webserver.
711      *
712      * @param thisWebUser   The username of the webserver
713      */
714     public void setWebUser( String thisWebUser )
715     {
716         this.webUser = thisWebUser;
717     }
718 
719     /**
720      * Sets the password to log into a secured JIRA.
721      *
722      * @param thisJiraPassword  The password for JIRA
723      */
724     public void setJiraPassword( final String thisJiraPassword )
725     {
726         this.jiraPassword = thisJiraPassword;
727     }
728 
729     /**
730      * Sets the username to log into a secured JIRA.
731      *
732      * @param thisJiraUser  The username for JIRA
733      */
734     public void setJiraUser( String thisJiraUser )
735     {
736         this.jiraUser = thisJiraUser;
737     }
738 
739     /**
740      * Sets the filter to apply to query to JIRA.
741      *
742      * @param thisFilter  The filter to query JIRA
743      */
744     public void setFilter( String thisFilter )
745     {
746         this.filter = thisFilter;
747     }
748 
749     /**
750      * Sets the component(s) to apply to query JIRA.
751      *
752      * @param theseComponents   The id(s) of components to show, as comma separated string
753      */
754     public void setComponent( String theseComponents )
755     {
756         this.component = theseComponents;
757     }
758 
759     /**
760      * Sets the fix version id(s) to apply to query JIRA.
761      *
762      * @param theseFixVersionIds The id(s) of fix versions to show, as comma separated string
763      */
764     public void setFixVersionIds( String theseFixVersionIds )
765     {
766         this.fixVersionIds = theseFixVersionIds;
767     }
768 
769     /**
770      * Sets the typeIds.
771      *
772      * @param theseTypeIds  The id(s) of the types to show, as comma separated string
773      */
774     public void setTypeIds( String theseTypeIds )
775     {
776         typeIds = theseTypeIds;
777     }
778 
779     public void setLog( Log log )
780     {
781         this.log = log;
782     }
783 
784     private Log getLog()
785     {
786         return log;
787     }
788 
789     public void setSettings( Settings settings )
790     {
791         this.settings = settings;
792     }
793 
794     public boolean isUseJql()
795     {
796         return useJql;
797     }
798 
799     public void setUseJql( boolean useJql )
800     {
801         this.useJql = useJql;
802     }
803 
804     public boolean isOnlyCurrentVersion()
805     {
806         return onlyCurrentVersion;
807     }
808 
809     public void setOnlyCurrentVersion( boolean onlyCurrentVersion )
810     {
811         this.onlyCurrentVersion = onlyCurrentVersion;
812     }
813 
814     public String getVersionPrefix()
815     {
816         return versionPrefix;
817     }
818 
819     public void setVersionPrefix( String versionPrefix )
820     {
821         this.versionPrefix = versionPrefix;
822     }
823 }