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.params.HttpClientParams;
32  import org.apache.commons.httpclient.auth.AuthScope;
33  import org.apache.commons.httpclient.methods.GetMethod;
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.plugin.issues.Issue;
36  import org.apache.maven.plugin.logging.Log;
37  import org.apache.maven.project.MavenProject;
38  import org.apache.maven.settings.Proxy;
39  import org.apache.maven.settings.Settings;
40  import org.apache.maven.wagon.proxy.ProxyInfo;
41  import org.codehaus.plexus.util.IOUtil;
42  import org.codehaus.plexus.util.StringUtils;
43  
44  import java.io.File;
45  import java.io.FileOutputStream;
46  import java.io.IOException;
47  import java.io.InputStream;
48  import java.io.OutputStream;
49  import java.net.MalformedURLException;
50  import java.net.URL;
51  import java.net.URLEncoder;
52  import java.util.Collections;
53  import java.util.HashMap;
54  import java.util.List;
55  import java.util.Locale;
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.html 816603 2012-05-08 12:53:30Z hboutemy $
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     /** Mapping containing all allowed JIRA status values. */
106     protected final Map<String,String> statusMap = new HashMap<String,String>( 8 );
107     /** Mapping containing all allowed JIRA resolution values. */
108     protected final Map<String,String> resolutionMap = new HashMap<String,String>( 8 );
109     /** Mapping containing all allowed JIRA priority values. */
110     protected final Map<String,String> priorityMap = new HashMap<String,String>( 8 );
111     /** Mapping containing all allowed JIRA type values. */
112     protected final Map<String,String> typeMap = new HashMap<String,String>( 8 );
113     /** The pattern used to parse dates from the JIRA xml file. */
114     protected String jiraDatePattern;
115 
116     /**
117      * Creates a filter given the parameters and some defaults.
118      *
119      * @return request parameters to be added to URL used for downloading the JIRA issues
120      */
121     private String createFilter()
122     {
123         // If the user has defined a filter - use that
124         if ( ( this.filter != null ) && ( this.filter.length() > 0 ) )
125         {
126             return this.filter;
127         }
128 
129         StringBuffer localFilter = new StringBuffer( 16 );
130 
131         // add fix versions
132         if ( fixVersionIds != null )
133         {
134             String[] fixVersions = fixVersionIds.split( "," );
135 
136             for ( int i = 0; i < fixVersions.length; i++ )
137             {
138                 if ( fixVersions[i].length() > 0 )
139                 {
140                     localFilter.append( "&fixfor=" ).append( fixVersions[i].trim() );
141                 }
142             }
143         }
144 
145         // get the Status Ids
146         if ( statusIds != null )
147         {
148             String[] stats = statusIds.split( "," );
149             for ( String stat : stats )
150             {
151                 stat = stat.trim();
152                 String statusParam = statusMap.get( stat );
153 
154                 if ( statusParam != null )
155                 {
156                     localFilter.append( "&statusIds=" ).append( statusParam );
157                 }
158                 else
159                 {
160                     // if it's numeric we can handle it too.
161                     try
162                     {
163                         Integer.parseInt( stat );
164                         localFilter.append( "&statusIds=" ).append( stat );
165                     }
166                     catch ( NumberFormatException nfe )
167                     {
168                         getLog().error( "maven-changes-plugin: invalid statusId " + stat );
169                     }
170                 }
171             }
172         }
173 
174         // get the Priority Ids
175         if ( priorityIds != null )
176         {
177             String[] prios = priorityIds.split( "," );
178 
179             for ( String prio : prios ) 
180             {
181                 prio = prio.trim();
182                 String priorityParam = priorityMap.get( prio );
183 
184                 if ( priorityParam != null )
185                 {
186                     localFilter.append( "&priorityIds=" ).append( priorityParam );
187                 }
188             }
189         }
190 
191         // get the Resolution Ids
192         if ( resolutionIds != null )
193         {
194             String[] resos = resolutionIds.split( "," );
195 
196             for ( String reso : resos ) 
197             {
198                 reso = reso.trim();
199                 String resoParam = resolutionMap.get( reso );
200 
201                 if ( resoParam != null )
202                 {
203                     localFilter.append( "&resolutionIds=" ).append( resoParam );
204                 }
205             }
206         }
207 
208         // add components
209         if ( component != null )
210         {
211             String[] components = component.split( "," );
212 
213             for ( String component : components ) 
214             {
215                 component = component.trim();
216                 if ( component.length() > 0 )
217                 {
218                     localFilter.append( "&component=" ).append( component );
219                 }
220             }
221         }
222 
223         // get the Type Ids
224         if ( typeIds != null )
225         {
226             String[] types = typeIds.split( "," );
227 
228             for ( String type : types )
229             {
230                 String typeParam = typeMap.get( type.trim() );
231 
232                 if ( typeParam != null )
233                 {
234                     localFilter.append( "&type=" ).append( typeParam );
235                 }
236             }
237         }
238 
239         // get the Sort order
240         int validSortColumnNames = 0;
241         if ( sortColumnNames != null )
242         {
243             String[] sortColumnNamesArray = sortColumnNames.split( "," );
244             // N.B. Add in reverse order (it's the way JIRA 3 likes it!!)
245             for ( int i = sortColumnNamesArray.length - 1; i >= 0; i-- )
246             {
247                 String lowerColumnName = sortColumnNamesArray[i].trim().toLowerCase( Locale.ENGLISH );
248                 boolean descending = false;
249                 String fieldName = null;
250                 if ( lowerColumnName.endsWith( "desc" ) )
251                 {
252                     descending = true;
253                     lowerColumnName = lowerColumnName.substring( 0, lowerColumnName.length() - 4 ).trim();
254                 }
255                 else if ( lowerColumnName.endsWith( "asc" ) )
256                 {
257                     descending = false;
258                     lowerColumnName = lowerColumnName.substring( 0, lowerColumnName.length() - 3 ).trim();
259                 }
260 
261                 if ( "key".equals( lowerColumnName ) )
262                 {
263                     fieldName = "issuekey";
264                 }
265                 else if ( "summary".equals( lowerColumnName ) )
266                 {
267                     fieldName = lowerColumnName;
268                 }
269                 else if ( "status".equals( lowerColumnName ) )
270                 {
271                     fieldName = lowerColumnName;
272                 }
273                 else if ( "resolution".equals( lowerColumnName ) )
274                 {
275                     fieldName = lowerColumnName;
276                 }
277                 else if ( "assignee".equals( lowerColumnName ) )
278                 {
279                     fieldName = lowerColumnName;
280                 }
281                 else if ( "reporter".equals( lowerColumnName ) )
282                 {
283                     fieldName = lowerColumnName;
284                 }
285                 else if ( "type".equals( lowerColumnName ) )
286                 {
287                     fieldName = "issuetype";
288                 }
289                 else if ( "priority".equals( lowerColumnName ) )
290                 {
291                     fieldName = lowerColumnName;
292                 }
293                 else if ( "version".equals( lowerColumnName ) )
294                 {
295                     fieldName = "versions";
296                 }
297                 else if ( "fix version".equals( lowerColumnName ) )
298                 {
299                     fieldName = "fixVersions";
300                 }
301                 else if ( "component".equals( lowerColumnName ) )
302                 {
303                     fieldName = "components";
304                 }
305                 else if ( "created".equals( lowerColumnName ) )
306                 {
307                     fieldName = lowerColumnName;
308                 }
309                 else if ( "updated".equals( lowerColumnName ) )
310                 {
311                     fieldName = lowerColumnName;
312                 }
313                 if ( fieldName != null )
314                 {
315                     localFilter.append( "&sorter/field=" );
316                     localFilter.append( fieldName );
317                     localFilter.append( "&sorter/order=" );
318                     localFilter.append( descending ? "DESC" : "ASC" );
319                     validSortColumnNames++;
320                 }
321                 else
322                 {
323                     // Error in the configuration
324                     getLog().error(
325                         "maven-changes-plugin: The configured value '" + lowerColumnName
326                             + "' for sortColumnNames is not correct." );
327                 }
328             }
329         }
330         if ( validSortColumnNames == 0 )
331         {
332             // Error in the configuration
333             getLog().error(
334                 "maven-changes-plugin: None of the configured sortColumnNames '" + sortColumnNames + "' are correct." );
335         }
336 
337 
338         return localFilter.toString();
339     }
340 
341     /**
342      * Execute the query on the JIRA server.
343      *
344      * @throws Exception on error
345      */
346     public void doExecute()
347         throws Exception
348     {
349         try
350         {
351             HttpClient client = new HttpClient();
352 
353             // MCHANGES-89 Allow circular redirects
354             HttpClientParams clientParams = client.getParams();
355             clientParams.setBooleanParameter( HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true );
356 
357             HttpState state = new HttpState();
358 
359             HostConfiguration hc = new HostConfiguration();
360 
361             client.setHostConfiguration( hc );
362 
363             client.setState( state );
364 
365             Map<String,String> urlMap = JiraHelper.getJiraUrlAndProjectId( project.getIssueManagement().getUrl() );
366 
367             String jiraUrl = urlMap.get( "url" );
368             getLog().debug( "JIRA lives at: " + jiraUrl );
369 
370             String jiraId = urlMap.get( "id" );
371 
372             determineProxy( jiraUrl, client );
373 
374             prepareBasicAuthentication( client );
375 
376             boolean jiraAuthenticationSuccessful = false;
377             if ( isJiraAuthenticationConfigured() )
378             {
379                 jiraAuthenticationSuccessful = doJiraAuthentication( client, jiraUrl );
380             }
381 
382             if ( ( isJiraAuthenticationConfigured() && jiraAuthenticationSuccessful )
383                 || !isJiraAuthenticationConfigured() )
384             {
385                 if ( jiraId == null || jiraId.length() == 0 )
386                 {
387                     log.debug( "The JIRA URL " + project.getIssueManagement().getUrl()
388                         + " doesn't include a pid, trying to extract it from JIRA." );
389                     jiraId = JiraHelper.getPidFromJira( log, project.getIssueManagement().getUrl(), client );
390                 }
391 
392                 if ( jiraId == null )
393                 {
394                     getLog().error( "The issue management URL in the POM does not include a pid,"
395                         + " and it was not possible to extract it from the page at that URL." );
396                 }
397                 else
398                 {
399                     // create the URL for getting the proper issues from JIRA
400                     String fullURL = jiraUrl + "/secure/IssueNavigator.jspa?view=rss&pid=" + jiraId;
401 
402                     if ( getFixFor() != null )
403                     {
404                         fullURL += "&fixfor=" + getFixFor();
405                     }
406 
407                     String createdFilter = createFilter();
408                     if ( createdFilter.charAt( 0 ) != '&' )
409                     {
410                         fullURL += "&";
411                     }
412                     fullURL += createdFilter;
413 
414                     fullURL += ( "&tempMax=" + nbEntriesMax + "&reset=true&decorator=none" );
415 
416                     if ( log.isDebugEnabled() )
417                     {
418                         log.debug( "download jira issues from url " + fullURL );
419                     }
420 
421                     // execute the GET
422                     download( client, fullURL );
423                 }
424             }
425         }
426         catch ( Exception e )
427         {
428             if ( project.getIssueManagement() != null )
429             {
430                 getLog().error( "Error accessing " + project.getIssueManagement().getUrl(), e );
431             }
432             else
433             {
434                 getLog().error( "Error accessing mock project issues", e );
435             }
436         }
437     }
438 
439     /**
440      * Override this method if you need to get issues for a specific Fix For.
441      *
442      * @return A Fix For id or <code>null</code> if you don't have that need
443      */
444     protected String getFixFor()
445     {
446         return null;
447     }
448 
449     /**
450      * Check and prepare for basic authentication.
451      *
452      * @param client The client to prepare
453      */
454     private void prepareBasicAuthentication( HttpClient client )
455     {
456         if ( ( webUser != null ) && ( webUser.length() > 0 ) )
457         {
458             client.getParams().setAuthenticationPreemptive( true );
459 
460             Credentials defaultcreds = new UsernamePasswordCredentials( webUser, webPassword );
461 
462             getLog().debug( "Using username: " + webUser + " for Basic Authentication." );
463 
464             client.getState().setCredentials( new AuthScope( null, AuthScope.ANY_PORT, null, AuthScope.ANY_SCHEME ),
465                                               defaultcreds );
466         }
467     }
468 
469     /**
470      * Authenticate against JIRA. This method relies on jiraUser and
471      * jiraPassword being set. You can check this by calling
472      * isJiraAuthenticationConfigured().
473      *
474      * @param client    the HttpClient
475      * @param jiraUrl   the JIRA installation
476      * @return <code>true</code> if the authentication was successful, otherwise <code>false</code>
477      */
478     private boolean doJiraAuthentication( HttpClient client, final String jiraUrl )
479     {
480         // log into JIRA if we have to
481         String loginUrl = null;
482 
483         StringBuffer loginLink = new StringBuffer( jiraUrl );
484 
485         loginLink.append( "/login.jsp?os_destination=/secure/" );
486 
487         try
488         {
489             loginLink.append( "&os_username=" ).append( URLEncoder.encode( jiraUser, UTF_8 ) );
490 
491             String password = null;
492             if ( jiraPassword != null )
493             {
494                 password = StringUtils.repeat( "*", jiraPassword.length() );
495             }
496             getLog().debug( "Login URL: " + loginLink + "&os_password=" + password );
497 
498             loginLink.append( "&os_password=" ).append( URLEncoder.encode( jiraPassword, UTF_8 ) );
499 
500             loginUrl = loginLink.toString();
501 
502             // execute the login
503             GetMethod loginGet = new GetMethod( loginUrl );
504 
505             client.executeMethod( loginGet );
506 
507             if ( loginSucceeded( loginGet ) )
508             {
509                 getLog().debug( "Successfully logged in into JIRA." );
510                 return true;
511             }
512             else
513             {
514                 getLog().warn( "Was unable to login into JIRA: wrong username and/or password." );
515             }
516         }
517         catch ( Exception e )
518         {
519             if ( getLog().isDebugEnabled() )
520             {
521                 getLog().error( "Error trying to login into JIRA.", e );
522             }
523             else
524             {
525                 getLog().error( "Error trying to login into JIRA. Cause is: " + e.getLocalizedMessage() );
526             }
527         }
528         return false;
529     }
530 
531     /**
532      * Check to see if we think that JIRA authentication is needed.
533      *
534      * @return <code>true</code> if jiraUser and jiraPassword are set, otherwise <code>false</code>
535      */
536     private boolean isJiraAuthenticationConfigured()
537     {
538         return ( jiraUser != null ) && ( jiraUser.length() > 0 ) && ( jiraPassword != null );
539     }
540 
541     /**
542      * Evaluate if the login attempt to JIRA was successful or not. We can't
543      * use the status code because JIRA returns 200 even if the login fails.
544      *
545      * @param loginGet The method that was executed
546      * @return <code>false</code> if we find an error message in the response body, otherwise <code>true</code>
547      * @todo There must be a nicer way to know whether we were able to login or not
548      */
549     private boolean loginSucceeded( GetMethod loginGet )
550         throws IOException
551     {
552         final String loginFailureResponse = "your username and password are incorrect";
553 
554         return loginGet.getResponseBodyAsString().indexOf( loginFailureResponse ) == -1;
555     }
556 
557     /**
558      * Setup proxy access if we have to.
559      *
560      * @param client  the HttpClient
561      */
562     private void determineProxy( String jiraUrl, HttpClient client )
563     {
564         // see whether there is any proxy defined in maven
565         Proxy proxy = null;
566 
567         String proxyHost = null;
568 
569         int proxyPort = 0;
570 
571         String proxyUser = null;
572 
573         String proxyPass = null;
574 
575         if ( project == null )
576         {
577             getLog().error( "No project set. No proxy info available." );
578 
579             return;
580         }
581 
582         if ( settings != null )
583         {
584             proxy = settings.getActiveProxy();
585         }
586 
587         if ( proxy != null )
588         {
589 
590             ProxyInfo proxyInfo = new ProxyInfo();
591             proxyInfo.setNonProxyHosts( proxy.getNonProxyHosts() );
592 
593             // Get the host out of the JIRA URL
594             URL url = null;
595             try
596             {
597                 url = new URL( jiraUrl );
598             }
599             catch( MalformedURLException e )
600             {
601                 getLog().error( "Invalid JIRA URL: " + jiraUrl + ". " + e.getMessage() );
602             }
603             String jiraHost = null;
604             if ( url != null )
605             {
606                 jiraHost = url.getHost();
607             }
608 
609             // Validation of proxy method copied from org.apache.maven.wagon.proxy.ProxyUtils.
610             // @todo Can use original when maven-changes-plugin requires a more recent version of Maven
611 
612             //if ( ProxyUtils.validateNonProxyHosts( proxyInfo, jiraHost ) )
613             if ( JiraHelper.validateNonProxyHosts( proxyInfo, jiraHost ) )
614             {
615                 return;
616             }
617 
618             proxyHost = settings.getActiveProxy().getHost();
619 
620             proxyPort = settings.getActiveProxy().getPort();
621 
622             proxyUser = settings.getActiveProxy().getUsername();
623 
624             proxyPass = settings.getActiveProxy().getPassword();
625 
626             getLog().debug( proxyPass );
627         }
628 
629         if ( proxyHost != null )
630         {
631             client.getHostConfiguration().setProxy( proxyHost, proxyPort );
632 
633             getLog().debug( "Using proxy: " + proxyHost + " at port " + proxyPort );
634 
635             if ( proxyUser != null )
636             {
637                 getLog().debug( "Using proxy user: " + proxyUser );
638 
639                 client.getState().setProxyCredentials(
640                                                        new AuthScope( null, AuthScope.ANY_PORT, null,
641                                                                       AuthScope.ANY_SCHEME ),
642                                                        new UsernamePasswordCredentials( proxyUser, proxyPass ) );
643             }
644         }
645     }
646 
647     /**
648      * Downloads the given link using the configured HttpClient, possibly following redirects.
649      *
650      * @param cl     the HttpClient
651      * @param link   the URL to JIRA
652      */
653     private void download( final HttpClient cl, final String link )
654     {
655         try
656         {
657             GetMethod gm = new GetMethod( link );
658 
659             getLog().info( "Downloading from JIRA at: " + link );
660 
661             gm.setFollowRedirects( true );
662 
663             cl.executeMethod( gm );
664 
665             StatusLine sl = gm.getStatusLine();
666 
667             if ( sl == null )
668             {
669                 getLog().error( "Unknown error validating link: " + link );
670 
671                 return;
672             }
673 
674             // if we get a redirect, do so
675             if ( gm.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY )
676             {
677                 Header locationHeader = gm.getResponseHeader( "Location" );
678 
679                 if ( locationHeader == null )
680                 {
681                     getLog().warn( "Site sent redirect, but did not set Location header" );
682                 }
683                 else
684                 {
685                     String newLink = locationHeader.getValue();
686 
687                     getLog().debug( "Following redirect to " + newLink );
688 
689                     download( cl, newLink );
690                 }
691             }
692 
693             if ( gm.getStatusCode() == HttpStatus.SC_OK )
694             {
695                 final InputStream responseBodyStream = gm.getResponseBodyAsStream();
696 
697                 if ( !output.getParentFile().exists() )
698                 {
699                     output.getParentFile().mkdirs();
700                 }
701 
702                 // write the response to file
703                 OutputStream out = null;
704                 try
705                 {
706                     out = new FileOutputStream( output );
707                     IOUtil.copy( responseBodyStream, out );
708                 }
709                 finally
710                 {
711                     IOUtil.close( out );
712                     IOUtil.close( responseBodyStream );
713                 }
714 
715                 getLog().debug( "Downloading from JIRA was successful" );
716             }
717             else
718             {
719                 getLog().warn( "Downloading from JIRA failed. Received: [" + gm.getStatusCode() + "]" );
720             }
721         }
722         catch ( HttpException e )
723         {
724             if ( getLog().isDebugEnabled() )
725             {
726                 getLog().error( "Error downloading issues from JIRA:", e );
727             }
728             else
729             {
730                 getLog().error( "Error downloading issues from JIRA url: " + e.getLocalizedMessage() );
731 
732             }
733         }
734         catch ( IOException e )
735         {
736             if ( getLog().isDebugEnabled() )
737             {
738                 getLog().error( "Error downloading issues from JIRA:", e );
739             }
740             else
741             {
742                 getLog().error( "Error downloading issues from JIRA. Cause is " + e.getLocalizedMessage() );
743             }
744         }
745     }
746 
747     public List<Issue> getIssueList()
748         throws MojoExecutionException
749     {
750         if ( output.isFile() )
751         {
752             JiraXML jira = new JiraXML( log, jiraDatePattern );
753             jira.parseXML( output );
754             getLog().info( "The JIRA version is '" + jira.getJiraVersion() + "'" );
755             return jira.getIssueList();
756         }
757         else
758         {
759             getLog().warn( "JIRA file " + output.getPath() + " doesn't exist." );
760             return Collections.emptyList();
761         }
762     }
763 
764     public void setJiraDatePattern( String jiraDatePattern )
765     {
766         this.jiraDatePattern = jiraDatePattern;
767     }
768 
769     /**
770      * Set the output file for the log.
771      *
772      * @param thisOutput the output file
773      */
774     public void setOutput( File thisOutput )
775     {
776         this.output = thisOutput;
777     }
778 
779     public File getOutput()
780     {
781         return this.output;
782     }
783 
784     /**
785      * Sets the project.
786      *
787      * @param thisProject  The project to set
788      */
789     public void setMavenProject( Object thisProject )
790     {
791         this.project = (MavenProject) thisProject;
792     }
793 
794     /**
795      * Sets the maximum number of Issues to show.
796      *
797      * @param nbEntries  The maximum number of Issues
798      */
799     public void setNbEntries( final int nbEntries )
800     {
801         nbEntriesMax = nbEntries;
802     }
803 
804     /**
805      * Sets the statusIds.
806      *
807      * @param thisStatusIds   The id(s) of the status to show, as comma separated string
808      */
809     public void setStatusIds( String thisStatusIds )
810     {
811         statusIds = thisStatusIds;
812     }
813 
814     /**
815      * Sets the priorityIds.
816      *
817      * @param thisPriorityIds  The id(s) of the priority to show, as comma separated string
818      */
819     public void setPriorityIds( String thisPriorityIds )
820     {
821         priorityIds = thisPriorityIds;
822     }
823 
824     /**
825      * Sets the resolutionIds.
826      *
827      * @param thisResolutionIds  The id(s) of the resolution to show, as comma separated string
828      */
829     public void setResolutionIds( String thisResolutionIds )
830     {
831         resolutionIds = thisResolutionIds;
832     }
833 
834     /**
835      * Sets the sort column names.
836      *
837      * @param thisSortColumnNames The column names to sort by
838      */
839     public void setSortColumnNames( String thisSortColumnNames )
840     {
841         sortColumnNames = thisSortColumnNames;
842     }
843 
844     /**
845      * Sets the password for authentication against the webserver.
846      *
847      * @param thisWebPassword  The password of the webserver
848      */
849     public void setWebPassword( String thisWebPassword )
850     {
851         this.webPassword = thisWebPassword;
852     }
853 
854     /**
855      * Sets the username for authentication against the webserver.
856      *
857      * @param thisWebUser   The username of the webserver
858      */
859     public void setWebUser( String thisWebUser )
860     {
861         this.webUser = thisWebUser;
862     }
863 
864     /**
865      * Sets the password to log into a secured JIRA.
866      *
867      * @param thisJiraPassword  The password for JIRA
868      */
869     public void setJiraPassword( final String thisJiraPassword )
870     {
871         this.jiraPassword = thisJiraPassword;
872     }
873 
874     /**
875      * Sets the username to log into a secured JIRA.
876      *
877      * @param thisJiraUser  The username for JIRA
878      */
879     public void setJiraUser( String thisJiraUser )
880     {
881         this.jiraUser = thisJiraUser;
882     }
883 
884     /**
885      * Sets the filter to apply to query to JIRA.
886      *
887      * @param thisFilter  The filter to query JIRA
888      */
889     public void setFilter( String thisFilter )
890     {
891         this.filter = thisFilter;
892     }
893 
894     /**
895      * Sets the component(s) to apply to query JIRA.
896      *
897      * @param theseComponents   The id(s) of components to show, as comma separated string
898      */
899     public void setComponent( String theseComponents )
900     {
901         this.component = theseComponents;
902     }
903 
904     /**
905      * Sets the fix version id(s) to apply to query JIRA.
906      *
907      * @param theseFixVersionIds The id(s) of fix versions to show, as comma separated string
908      */
909     public void setFixVersionIds( String theseFixVersionIds )
910     {
911         this.fixVersionIds = theseFixVersionIds;
912     }
913 
914     /**
915      * Sets the typeIds.
916      *
917      * @param theseTypeIds  The id(s) of the types to show, as comma separated string
918      */
919     public void setTypeIds( String theseTypeIds )
920     {
921         typeIds = theseTypeIds;
922     }
923 
924     public void setLog( Log log )
925     {
926         this.log = log;
927     }
928 
929     private Log getLog()
930     {
931         return log;
932     }
933 
934     public void setSettings( Settings settings )
935     {
936         this.settings = settings;
937     }
938 }