View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.changes.jira;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.StringWriter;
24  import java.net.URI;
25  import java.text.ParseException;
26  import java.text.SimpleDateFormat;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Collections;
30  import java.util.Date;
31  import java.util.List;
32  import java.util.Map;
33  
34  import com.fasterxml.jackson.core.JsonFactory;
35  import com.fasterxml.jackson.core.JsonGenerator;
36  import com.fasterxml.jackson.core.JsonParser;
37  import com.fasterxml.jackson.databind.JsonNode;
38  import com.fasterxml.jackson.databind.MappingJsonFactory;
39  import org.apache.http.HttpHeaders;
40  import org.apache.http.HttpHost;
41  import org.apache.http.HttpResponse;
42  import org.apache.http.HttpStatus;
43  import org.apache.http.auth.AuthScope;
44  import org.apache.http.auth.UsernamePasswordCredentials;
45  import org.apache.http.client.CredentialsProvider;
46  import org.apache.http.client.config.RequestConfig;
47  import org.apache.http.client.methods.CloseableHttpResponse;
48  import org.apache.http.client.methods.HttpGet;
49  import org.apache.http.client.methods.HttpPost;
50  import org.apache.http.entity.ContentType;
51  import org.apache.http.entity.StringEntity;
52  import org.apache.http.impl.client.BasicCookieStore;
53  import org.apache.http.impl.client.BasicCredentialsProvider;
54  import org.apache.http.impl.client.CloseableHttpClient;
55  import org.apache.http.impl.client.HttpClientBuilder;
56  import org.apache.http.impl.client.HttpClients;
57  import org.apache.http.message.BasicHeader;
58  import org.apache.maven.plugin.MojoExecutionException;
59  import org.apache.maven.plugin.MojoFailureException;
60  import org.apache.maven.plugin.logging.Log;
61  import org.apache.maven.plugins.changes.issues.Issue;
62  import org.apache.maven.plugins.changes.issues.IssueUtils;
63  import org.apache.maven.project.MavenProject;
64  import org.apache.maven.settings.Proxy;
65  import org.apache.maven.settings.Server;
66  import org.apache.maven.settings.Settings;
67  import org.apache.maven.settings.building.SettingsProblem;
68  import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
69  import org.apache.maven.settings.crypto.SettingsDecrypter;
70  import org.apache.maven.settings.crypto.SettingsDecryptionResult;
71  
72  /**
73   * Use the JIRA REST API to download issues. This class assumes that the URL points to a copy of JIRA that
74   * implements the REST API.
75   */
76  public class RestJiraDownloader {
77  
78      /** Log for debug output. */
79      private Log log;
80  
81      /** The maven project. */
82      private MavenProject project;
83  
84      /** The maven settings. */
85      private Settings settings;
86  
87      private SettingsDecrypter settingsDecrypter;
88  
89      /** Filter the JIRA query based on the current version */
90      private boolean onlyCurrentVersion;
91  
92      /** The versionPrefix to apply to the POM version */
93      protected String versionPrefix;
94  
95      /** The maximum number of entries to show. */
96      protected int nbEntriesMax;
97  
98      /** The filter to apply to query to JIRA. */
99      protected String filter;
100 
101     /** Ids of fix versions to show, as comma separated string. */
102     protected String fixVersionIds;
103 
104     /** Ids of status to show, as comma separated string. */
105     protected String statusIds;
106 
107     /** Ids of resolution to show, as comma separated string. */
108     protected String resolutionIds;
109 
110     /** Ids of priority to show, as comma separated string. */
111     protected String priorityIds;
112 
113     /** The component to show. */
114     protected String component;
115 
116     /** Ids of types to show, as comma separated string. */
117     protected String typeIds;
118 
119     /** Column names to sort by, as comma separated string. */
120     protected String sortColumnNames;
121 
122     /** The username to log into JIRA. */
123     protected String jiraUser;
124 
125     /** The password to log into JIRA. */
126     protected String jiraPassword;
127 
128     private String jiraServerId;
129 
130     /** The pattern used to parse dates from the JIRA xml file. */
131     protected String jiraDatePattern;
132 
133     protected int connectionTimeout;
134 
135     protected int receiveTimout;
136 
137     private List<Issue> issueList;
138 
139     private JsonFactory jsonFactory;
140 
141     private SimpleDateFormat dateFormat;
142 
143     private List<String> resolvedFixVersionIds;
144 
145     private List<String> resolvedStatusIds;
146 
147     private List<String> resolvedComponentIds;
148 
149     private List<String> resolvedTypeIds;
150 
151     private List<String> resolvedResolutionIds;
152 
153     private List<String> resolvedPriorityIds;
154 
155     /**
156      * Override this method if you need to get issues for a specific Fix For.
157      *
158      * @return a Fix For id or <code>null</code> if you don't have that need
159      */
160     protected String getFixFor() {
161         if (onlyCurrentVersion) {
162             // Let JIRA do the filtering of the current version instead of the JIRA mojo.
163             // This way JIRA returns less issues and we do not run into the "nbEntriesMax" limit that easily.
164 
165             String version = (versionPrefix == null ? "" : versionPrefix) + project.getVersion();
166 
167             // Remove "-SNAPSHOT" from the end of the version, if it's there
168             if (version.endsWith(IssueUtils.SNAPSHOT_SUFFIX)) {
169                 return version.substring(0, version.length() - IssueUtils.SNAPSHOT_SUFFIX.length());
170             } else {
171                 return version;
172             }
173         } else {
174             return null;
175         }
176     }
177 
178     /**
179      * Sets the project.
180      *
181      * @param thisProject The project to set
182      */
183     public void setMavenProject(MavenProject thisProject) {
184         this.project = thisProject;
185     }
186 
187     public void setLog(Log log) {
188         this.log = log;
189     }
190 
191     protected Log getLog() {
192         return log;
193     }
194 
195     public void setSettings(Settings settings) {
196         this.settings = settings;
197     }
198 
199     public void setSettingsDecrypter(SettingsDecrypter settingsDecrypter) {
200         this.settingsDecrypter = settingsDecrypter;
201     }
202 
203     public void setOnlyCurrentVersion(boolean onlyCurrentVersion) {
204         this.onlyCurrentVersion = onlyCurrentVersion;
205     }
206 
207     public void setVersionPrefix(String versionPrefix) {
208         this.versionPrefix = versionPrefix;
209     }
210 
211     public void setJiraDatePattern(String jiraDatePattern) {
212         this.jiraDatePattern = jiraDatePattern;
213     }
214 
215     /**
216      * Sets the maximum number of Issues to show.
217      *
218      * @param nbEntries The maximum number of Issues
219      */
220     public void setNbEntries(final int nbEntries) {
221         nbEntriesMax = nbEntries;
222     }
223 
224     /**
225      * Sets the statusIds.
226      *
227      * @param thisStatusIds The id(s) of the status to show, as comma separated string
228      */
229     public void setStatusIds(String thisStatusIds) {
230         statusIds = thisStatusIds;
231     }
232 
233     /**
234      * Sets the priorityIds.
235      *
236      * @param thisPriorityIds The id(s) of the priority to show, as comma separated string
237      */
238     public void setPriorityIds(String thisPriorityIds) {
239         priorityIds = thisPriorityIds;
240     }
241 
242     /**
243      * Sets the resolutionIds.
244      *
245      * @param thisResolutionIds The id(s) of the resolution to show, as comma separated string
246      */
247     public void setResolutionIds(String thisResolutionIds) {
248         resolutionIds = thisResolutionIds;
249     }
250 
251     /**
252      * Sets the sort column names.
253      *
254      * @param thisSortColumnNames The column names to sort by
255      */
256     public void setSortColumnNames(String thisSortColumnNames) {
257         sortColumnNames = thisSortColumnNames;
258     }
259 
260     /**
261      * Sets the password to log into a secured JIRA.
262      *
263      * @param thisJiraPassword The password for JIRA
264      */
265     public void setJiraPassword(final String thisJiraPassword) {
266         this.jiraPassword = thisJiraPassword;
267     }
268 
269     /**
270      * Sets the username to log into a secured JIRA.
271      *
272      * @param thisJiraUser The username for JIRA
273      */
274     public void setJiraUser(String thisJiraUser) {
275         this.jiraUser = thisJiraUser;
276     }
277 
278     public void setJiraServerId(String jiraServerId) {
279         this.jiraServerId = jiraServerId;
280     }
281 
282     /**
283      * Sets the filter to apply to query to JIRA.
284      *
285      * @param thisFilter The filter to query JIRA
286      */
287     public void setFilter(String thisFilter) {
288         this.filter = thisFilter;
289     }
290 
291     /**
292      * Sets the component(s) to apply to query JIRA.
293      *
294      * @param theseComponents The id(s) of components to show, as comma separated string
295      */
296     public void setComponent(String theseComponents) {
297         this.component = theseComponents;
298     }
299 
300     /**
301      * Sets the fix version id(s) to apply to query JIRA.
302      *
303      * @param theseFixVersionIds The id(s) of fix versions to show, as comma separated string
304      */
305     public void setFixVersionIds(String theseFixVersionIds) {
306         this.fixVersionIds = theseFixVersionIds;
307     }
308 
309     /**
310      * Sets the typeIds.
311      *
312      * @param theseTypeIds The id(s) of the types to show, as comma separated string
313      */
314     public void setTypeIds(String theseTypeIds) {
315         typeIds = theseTypeIds;
316     }
317 
318     public void setConnectionTimeout(int connectionTimeout) {
319         this.connectionTimeout = connectionTimeout;
320     }
321 
322     public void setReceiveTimout(int receiveTimout) {
323         this.receiveTimout = receiveTimout;
324     }
325 
326     public static class NoRest extends Exception {
327         private static final long serialVersionUID = 6970088805270319624L;
328 
329         public NoRest() {
330             // blank on purpose.
331         }
332 
333         public NoRest(String message) {
334             super(message);
335         }
336     }
337 
338     public RestJiraDownloader() {
339         jsonFactory = new MappingJsonFactory();
340         // 2012-07-17T06:26:47.723-0500
341         dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
342         resolvedFixVersionIds = new ArrayList<>();
343         resolvedStatusIds = new ArrayList<>();
344         resolvedComponentIds = new ArrayList<>();
345         resolvedTypeIds = new ArrayList<>();
346         resolvedResolutionIds = new ArrayList<>();
347         resolvedPriorityIds = new ArrayList<>();
348     }
349 
350     public void doExecute() throws Exception {
351 
352         Map<String, String> urlMap =
353                 JiraHelper.getJiraUrlAndProjectName(project.getIssueManagement().getUrl());
354         String jiraUrl = urlMap.get("url");
355         String jiraProject = urlMap.get("project");
356 
357         try (CloseableHttpClient client = setupHttpClient(jiraUrl)) {
358             checkRestApi(client, jiraUrl);
359             doSessionAuth(client, jiraUrl);
360             resolveIds(client, jiraUrl, jiraProject);
361             search(client, jiraProject, jiraUrl);
362         }
363     }
364 
365     private void search(CloseableHttpClient client, String jiraProject, String jiraUrl)
366             throws IOException, MojoExecutionException {
367         String jqlQuery = new JqlQueryBuilder(log)
368                 .urlEncode(false)
369                 .project(jiraProject)
370                 .fixVersion(getFixFor())
371                 .fixVersionIds(resolvedFixVersionIds)
372                 .statusIds(resolvedStatusIds)
373                 .priorityIds(resolvedPriorityIds)
374                 .resolutionIds(resolvedResolutionIds)
375                 .components(resolvedComponentIds)
376                 .typeIds(resolvedTypeIds)
377                 .sortColumnNames(sortColumnNames)
378                 .filter(filter)
379                 .build();
380 
381         log.debug("JIRA jql=" + jqlQuery);
382 
383         StringWriter searchParamStringWriter = new StringWriter();
384         try (JsonGenerator gen = jsonFactory.createGenerator(searchParamStringWriter)) {
385             gen.writeStartObject();
386             gen.writeStringField("jql", jqlQuery);
387             gen.writeNumberField("maxResults", nbEntriesMax);
388             gen.writeArrayFieldStart("fields");
389             // Retrieve all fields. If that seems slow, we can reconsider.
390             gen.writeString("*all");
391             gen.writeEndArray();
392             gen.writeEndObject();
393         }
394 
395         HttpPost httpPost = new HttpPost(jiraUrl + "/rest/api/2/search");
396         httpPost.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.getMimeType());
397         httpPost.setEntity(new StringEntity(searchParamStringWriter.toString()));
398 
399         try (CloseableHttpResponse response = client.execute(httpPost)) {
400 
401             if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
402                 reportErrors(response);
403             }
404 
405             JsonNode issueTree = getResponseTree(response);
406 
407             assertIsObject(issueTree);
408             JsonNode issuesNode = issueTree.get("issues");
409             assertIsArray(issuesNode);
410             buildIssues(issuesNode, jiraUrl);
411         }
412     }
413 
414     private void checkRestApi(CloseableHttpClient client, String jiraUrl) throws IOException, NoRest {
415         // We use version 2 of the REST API, that first appeared in JIRA 5
416         // Check if version 2 of the REST API is supported
417         // http://docs.atlassian.com/jira/REST/5.0/
418         // Note that serverInfo can always be accessed without authentication
419 
420         HttpGet httpGet = new HttpGet(jiraUrl + "/rest/api/2/serverInfo");
421         try (CloseableHttpResponse response = client.execute(httpGet)) {
422             if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
423                 throw new NoRest("This JIRA server does not support version 2 of the REST API, "
424                         + "which maven-changes-plugin requires.");
425             }
426         }
427     }
428 
429     private JsonNode getResponseTree(HttpResponse response) throws IOException {
430         try (InputStream inputStream = response.getEntity().getContent();
431                 JsonParser jsonParser = jsonFactory.createParser(inputStream)) {
432             return jsonParser.readValueAsTree();
433         }
434     }
435 
436     private void reportErrors(HttpResponse resp) throws IOException, MojoExecutionException {
437         ContentType contentType = ContentType.get(resp.getEntity());
438         if (contentType != null && contentType.getMimeType().equals(ContentType.APPLICATION_JSON.getMimeType())) {
439             JsonNode errorTree = getResponseTree(resp);
440             assertIsObject(errorTree);
441             JsonNode messages = errorTree.get("errorMessages");
442             if (messages != null) {
443                 for (int mx = 0; mx < messages.size(); mx++) {
444                     getLog().error(messages.get(mx).asText());
445                 }
446             } else {
447                 JsonNode message = errorTree.get("message");
448                 if (message != null) {
449                     getLog().error(message.asText());
450                 }
451             }
452         }
453         throw new MojoExecutionException(String.format(
454                 "Failed to query issues; response %d", resp.getStatusLine().getStatusCode()));
455     }
456 
457     private void resolveIds(CloseableHttpClient client, String jiraUrl, String jiraProject)
458             throws IOException, MojoExecutionException, MojoFailureException {
459         resolveList(
460                 resolvedComponentIds,
461                 client,
462                 "components",
463                 component,
464                 jiraUrl + "/rest/api/2/project/" + jiraProject + "/components");
465         resolveList(
466                 resolvedFixVersionIds,
467                 client,
468                 "fixVersions",
469                 fixVersionIds,
470                 jiraUrl + "/rest/api/2/project/" + jiraProject + "/versions");
471         resolveList(resolvedStatusIds, client, "status", statusIds, jiraUrl + "/rest/api/2/status");
472         resolveList(resolvedResolutionIds, client, "resolution", resolutionIds, jiraUrl + "/rest/api/2/resolution");
473         resolveList(resolvedTypeIds, client, "type", typeIds, jiraUrl + "/rest/api/2/issuetype");
474         resolveList(resolvedPriorityIds, client, "priority", priorityIds, jiraUrl + "/rest/api/2/priority");
475     }
476 
477     private void resolveList(
478             List<String> targetList, CloseableHttpClient client, String what, String input, String listRestUrlPattern)
479             throws IOException, MojoExecutionException, MojoFailureException {
480         if (input == null || input.isEmpty()) {
481             return;
482         }
483 
484         HttpGet httpGet = new HttpGet(listRestUrlPattern);
485 
486         try (CloseableHttpResponse response = client.execute(httpGet)) {
487             if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
488                 getLog().error(String.format("Could not get %s list from %s", what, listRestUrlPattern));
489                 reportErrors(response);
490             }
491 
492             JsonNode items = getResponseTree(response);
493             String[] pieces = input.split(",");
494             for (String item : pieces) {
495                 targetList.add(resolveOneItem(items, what, item.trim()));
496             }
497         }
498     }
499 
500     private String resolveOneItem(JsonNode items, String what, String nameOrId) throws MojoFailureException {
501         for (int cx = 0; cx < items.size(); cx++) {
502             JsonNode item = items.get(cx);
503             if (nameOrId.equals(item.get("id").asText())) {
504                 return nameOrId;
505             } else if (nameOrId.equals(item.get("name").asText())) {
506                 return item.get("id").asText();
507             }
508         }
509         throw new MojoFailureException(String.format("Could not find %s %s.", what, nameOrId));
510     }
511 
512     private void buildIssues(JsonNode issuesNode, String jiraUrl) {
513         issueList = new ArrayList<>();
514         for (int ix = 0; ix < issuesNode.size(); ix++) {
515             JsonNode issueNode = issuesNode.get(ix);
516             assertIsObject(issueNode);
517             Issue issue = new Issue();
518             JsonNode val;
519 
520             val = issueNode.get("id");
521             if (isNotNullNode(val)) {
522                 issue.setId(val.asText());
523             }
524 
525             val = issueNode.get("key");
526             if (isNotNullNode(val)) {
527                 issue.setKey(val.asText());
528                 issue.setLink(String.format("%s/browse/%s", jiraUrl, val.asText()));
529             }
530 
531             // much of what we want is in here.
532             JsonNode fieldsNode = issueNode.get("fields");
533 
534             val = fieldsNode.get("assignee");
535             processAssignee(issue, val);
536 
537             val = fieldsNode.get("created");
538             processCreated(issue, val);
539 
540             val = fieldsNode.get("components");
541             processComponents(issue, val);
542 
543             val = fieldsNode.get("fixVersions");
544             processFixVersions(issue, val);
545 
546             val = fieldsNode.get("issuetype");
547             processIssueType(issue, val);
548 
549             val = fieldsNode.get("priority");
550             processPriority(issue, val);
551 
552             val = fieldsNode.get("reporter");
553             processReporter(issue, val);
554 
555             val = fieldsNode.get("resolution");
556             processResolution(issue, val);
557 
558             val = fieldsNode.get("status");
559             processStatus(issue, val);
560 
561             val = fieldsNode.get("summary");
562             if (isNotNullNode(val)) {
563                 issue.setSummary(val.asText());
564             }
565 
566             val = fieldsNode.get("updated");
567             processUpdated(issue, val);
568 
569             val = fieldsNode.get("versions");
570             processVersions(issue, val);
571 
572             issueList.add(issue);
573         }
574     }
575 
576     private void processVersions(Issue issue, JsonNode val) {
577         StringBuilder sb = new StringBuilder();
578         if (isNotNullNode(val)) {
579             for (int vx = 0; vx < val.size(); vx++) {
580                 if (isNotNullNode(val.get(vx)) && isNotNullNode(val.get(vx).get("name"))) {
581                     sb.append(val.get(vx).get("name").asText());
582                     sb.append(", ");
583                 }
584             }
585         }
586         if (sb.length() > 0) {
587             // remove last ", "
588             issue.setVersion(sb.substring(0, sb.length() - 2));
589         }
590     }
591 
592     private void processStatus(Issue issue, JsonNode val) {
593         if (isNotNullNode(val) && isNotNullNode(val.get("name"))) {
594             issue.setStatus(val.get("name").asText());
595         }
596     }
597 
598     private void processPriority(Issue issue, JsonNode val) {
599         if (isNotNullNode(val) && isNotNullNode(val.get("name"))) {
600             issue.setPriority(val.get("name").asText());
601         }
602     }
603 
604     private void processResolution(Issue issue, JsonNode val) {
605         if (isNotNullNode(val) && isNotNullNode(val.get("name"))) {
606             issue.setResolution(val.get("name").asText());
607         }
608     }
609 
610     private String getPerson(JsonNode val) {
611         JsonNode nameNode = val.get("displayName");
612         if (!isNotNullNode(nameNode)) {
613             nameNode = val.get("name");
614         }
615         if (isNotNullNode(nameNode)) {
616             return nameNode.asText();
617         } else {
618             return null;
619         }
620     }
621 
622     private void processAssignee(Issue issue, JsonNode val) {
623         if (isNotNullNode(val)) {
624             String text = getPerson(val);
625             if (text != null) {
626                 issue.setAssignee(text);
627             }
628         }
629     }
630 
631     private void processReporter(Issue issue, JsonNode val) {
632         if (isNotNullNode(val)) {
633             String text = getPerson(val);
634             if (text != null) {
635                 issue.setReporter(text);
636             }
637         }
638     }
639 
640     private void processCreated(Issue issue, JsonNode val) {
641         if (isNotNullNode(val)) {
642             try {
643                 issue.setCreated(parseDate(val));
644             } catch (ParseException e) {
645                 getLog().warn("Invalid created date " + val.asText());
646             }
647         }
648     }
649 
650     private void processUpdated(Issue issue, JsonNode val) {
651         if (isNotNullNode(val)) {
652             try {
653                 issue.setUpdated(parseDate(val));
654             } catch (ParseException e) {
655                 getLog().warn("Invalid updated date " + val.asText());
656             }
657         }
658     }
659 
660     private Date parseDate(JsonNode val) throws ParseException {
661         return dateFormat.parse(val.asText());
662     }
663 
664     private void processFixVersions(Issue issue, JsonNode val) {
665         if (isNotNullNode(val)) {
666             assertIsArray(val);
667             for (int vx = 0; vx < val.size(); vx++) {
668                 JsonNode fvNode = val.get(vx);
669                 if (isNotNullNode(fvNode) && isNotNullNode(fvNode.get("name"))) {
670                     issue.addFixVersion(fvNode.get("name").asText());
671                 }
672             }
673         }
674     }
675 
676     private void processComponents(Issue issue, JsonNode val) {
677         if (isNotNullNode(val)) {
678             assertIsArray(val);
679             for (int cx = 0; cx < val.size(); cx++) {
680                 JsonNode cnode = val.get(cx);
681                 if (isNotNullNode(cnode) && isNotNullNode(cnode.get("name"))) {
682                     issue.addComponent(cnode.get("name").asText());
683                 }
684             }
685         }
686     }
687 
688     private void processIssueType(Issue issue, JsonNode val) {
689         if (isNotNullNode(val) && isNotNullNode(val.get("name"))) {
690             issue.setType(val.get("name").asText());
691         }
692     }
693 
694     private void assertIsObject(JsonNode node) {
695         if (!node.isObject()) {
696             throw new IllegalArgumentException("json node: " + node + " is not an object");
697         }
698     }
699 
700     private void assertIsArray(JsonNode node) {
701         if (!node.isArray()) {
702             throw new IllegalArgumentException("json node: " + node + " is not an array");
703         }
704     }
705 
706     private boolean isNotNullNode(JsonNode node) {
707         return node != null && !node.isNull();
708     }
709 
710     private void doSessionAuth(CloseableHttpClient client, String jiraUrl)
711             throws IOException, MojoExecutionException, NoRest {
712 
713         Server server = settings.getServer(jiraServerId);
714         if (server != null) {
715             SettingsDecryptionResult result = settingsDecrypter.decrypt(new DefaultSettingsDecryptionRequest(server));
716             if (!result.getProblems().isEmpty()) {
717                 for (SettingsProblem problem : result.getProblems()) {
718                     log.error(problem.getMessage());
719                 }
720             } else {
721                 jiraUser = result.getServer().getUsername();
722                 jiraPassword = result.getServer().getPassword();
723             }
724         }
725 
726         if (jiraUser != null) {
727             StringWriter jsWriter = new StringWriter();
728             try (JsonGenerator gen = jsonFactory.createGenerator(jsWriter)) {
729                 gen.writeStartObject();
730                 gen.writeStringField("username", jiraUser);
731                 gen.writeStringField("password", jiraPassword);
732                 gen.writeEndObject();
733             }
734 
735             HttpPost post = new HttpPost(jiraUrl + "/rest/auth/1/session");
736             post.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.getMimeType());
737             post.setEntity(new StringEntity(jsWriter.toString()));
738 
739             try (CloseableHttpResponse response = client.execute(post)) {
740                 int statusCode = response.getStatusLine().getStatusCode();
741                 if (statusCode != HttpStatus.SC_OK) {
742                     if (statusCode != HttpStatus.SC_UNAUTHORIZED && statusCode != HttpStatus.SC_FORBIDDEN) {
743                         // if not one of the documented failures, assume that there's no rest in there in the first
744                         // place.
745                         throw new NoRest();
746                     }
747                     throw new MojoExecutionException(String.format("Authentication failure status %d.", statusCode));
748                 }
749             }
750         }
751     }
752 
753     private CloseableHttpClient setupHttpClient(String jiraUrl) {
754 
755         HttpClientBuilder httpClientBuilder = HttpClients.custom()
756                 .setDefaultCookieStore(new BasicCookieStore())
757                 .setDefaultRequestConfig(RequestConfig.custom()
758                         .setConnectionRequestTimeout(receiveTimout)
759                         .setConnectTimeout(connectionTimeout)
760                         .build())
761                 .setDefaultHeaders(Collections.singletonList(new BasicHeader("Accept", "application/json")));
762 
763         Proxy proxy = getProxy(jiraUrl);
764         if (proxy != null) {
765             if (proxy.getUsername() != null && proxy.getPassword() != null) {
766                 CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
767                 credentialsProvider.setCredentials(
768                         new AuthScope(proxy.getHost(), proxy.getPort()),
769                         new UsernamePasswordCredentials(proxy.getUsername(), proxy.getPassword()));
770                 httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
771             }
772             httpClientBuilder.setProxy(new HttpHost(proxy.getHost(), proxy.getPort()));
773         }
774         return httpClientBuilder.build();
775     }
776 
777     private Proxy getProxy(String jiraUrl) {
778         Proxy proxy = settings.getActiveProxy();
779         if (proxy != null) {
780             SettingsDecryptionResult result = settingsDecrypter.decrypt(new DefaultSettingsDecryptionRequest(proxy));
781             if (!result.getProblems().isEmpty()) {
782                 for (SettingsProblem problem : result.getProblems()) {
783                     log.error(problem.getMessage());
784                 }
785             } else {
786                 proxy = result.getProxy();
787             }
788         }
789 
790         if (proxy != null && proxy.getNonProxyHosts() != null) {
791             URI jiraUri = URI.create(jiraUrl);
792             boolean nonProxy = Arrays.stream(proxy.getNonProxyHosts().split("\\|"))
793                     .anyMatch(host -> !host.equalsIgnoreCase(jiraUri.getHost()));
794             if (nonProxy) {
795                 return null;
796             }
797         }
798 
799         return proxy;
800     }
801 
802     public List<Issue> getIssueList() {
803         return issueList;
804     }
805 }