| 
1
 | 
 package pl.com.it_crowd.youtrack.api;
 | 
1
 | 
 package pl.com.it_crowd.youtrack.api;
 | 
| 
2
 | 
 
 | 
2
 | 
 
 | 
| 
 | 
   | 
3
 | 
+import org.apache.commons.logging.Log;
 | 
| 
 | 
   | 
4
 | 
+import org.apache.commons.logging.LogFactory;
 | 
| 
3
 | 
 import org.apache.http.Header;
 | 
5
 | 
 import org.apache.http.Header;
 | 
| 
4
 | 
 import org.apache.http.HttpEntity;
 | 
6
 | 
 import org.apache.http.HttpEntity;
 | 
| 
5
 | 
 import org.apache.http.HttpHeaders;
 | 
7
 | 
 import org.apache.http.HttpHeaders;
 | 
 | 
@@ -17,7 +19,6 @@ import org.apache.http.client.methods.HttpGet; | 
 | 
@@ -17,7 +19,6 @@ import org.apache.http.client.methods.HttpGet; | 
| 
17
 | 
 import org.apache.http.client.methods.HttpPost;
 | 
19
 | 
 import org.apache.http.client.methods.HttpPost;
 | 
| 
18
 | 
 import org.apache.http.client.methods.HttpPut;
 | 
20
 | 
 import org.apache.http.client.methods.HttpPut;
 | 
| 
19
 | 
 import org.apache.http.client.methods.HttpUriRequest;
 | 
21
 | 
 import org.apache.http.client.methods.HttpUriRequest;
 | 
| 
20
 | 
-import org.apache.http.client.utils.URIBuilder;
 | 
 | 
   | 
| 
21
 | 
 import org.apache.http.conn.ClientConnectionManager;
 | 
22
 | 
 import org.apache.http.conn.ClientConnectionManager;
 | 
| 
22
 | 
 import org.apache.http.conn.scheme.Scheme;
 | 
23
 | 
 import org.apache.http.conn.scheme.Scheme;
 | 
| 
23
 | 
 import org.apache.http.conn.scheme.SchemeRegistry;
 | 
24
 | 
 import org.apache.http.conn.scheme.SchemeRegistry;
 | 
 | 
@@ -60,6 +61,8 @@ import java.util.List; | 
 | 
@@ -60,6 +61,8 @@ import java.util.List; | 
| 
60
 | 
 import java.util.regex.Matcher;
 | 
61
 | 
 import java.util.regex.Matcher;
 | 
| 
61
 | 
 import java.util.regex.Pattern;
 | 
62
 | 
 import java.util.regex.Pattern;
 | 
| 
62
 | 
 
 | 
63
 | 
 
 | 
| 
 | 
   | 
64
 | 
+import static pl.com.it_crowd.youtrack.api.URIUtils.buildURI;
 | 
| 
 | 
   | 
65
 | 
+
 | 
| 
63
 | 
 public class YoutrackAPI {
 | 
66
 | 
 public class YoutrackAPI {
 | 
| 
64
 | 
 // ------------------------------ FIELDS ------------------------------
 | 
67
 | 
 // ------------------------------ FIELDS ------------------------------
 | 
| 
65
 | 
 
 | 
68
 | 
 
 | 
 | 
@@ -67,10 +70,14 @@ public class YoutrackAPI { | 
 | 
@@ -67,10 +70,14 @@ public class YoutrackAPI { | 
| 
67
 | 
 
 | 
70
 | 
 
 | 
| 
68
 | 
   private final static QName Issue_QNAME = new QName("", "issue");
 | 
71
 | 
   private final static QName Issue_QNAME = new QName("", "issue");
 | 
| 
69
 | 
 
 | 
72
 | 
 
 | 
| 
 | 
   | 
73
 | 
+  private static Log LOG = LogFactory.getLog(YoutrackAPI.class);
 | 
| 
 | 
   | 
74
 | 
+
 | 
| 
70
 | 
   private HttpClient httpClient;
 | 
75
 | 
   private HttpClient httpClient;
 | 
| 
71
 | 
 
 | 
76
 | 
 
 | 
| 
72
 | 
   private String serviceLocation;
 | 
77
 | 
   private String serviceLocation;
 | 
| 
73
 | 
 
 | 
78
 | 
 
 | 
| 
 | 
   | 
79
 | 
+  private URI serviceLocationURI;
 | 
| 
 | 
   | 
80
 | 
+
 | 
| 
74
 | 
 // -------------------------- STATIC METHODS --------------------------
 | 
81
 | 
 // -------------------------- STATIC METHODS --------------------------
 | 
| 
75
 | 
 
 | 
82
 | 
 
 | 
| 
76
 | 
   private static HttpClient getDefaultHttpClient()
 | 
83
 | 
   private static HttpClient getDefaultHttpClient()
 | 
 | 
@@ -134,7 +141,15 @@ public class YoutrackAPI { | 
 | 
@@ -134,7 +141,15 @@ public class YoutrackAPI { | 
| 
134
 | 
 
 | 
141
 | 
 
 | 
| 
135
 | 
   public YoutrackAPI(String serviceLocation, HttpClient httpClient)
 | 
142
 | 
   public YoutrackAPI(String serviceLocation, HttpClient httpClient)
 | 
| 
136
 | 
   {
 | 
143
 | 
   {
 | 
| 
 | 
   | 
144
 | 
+    if (serviceLocation == null) {
 | 
| 
 | 
   | 
145
 | 
+      throw new IllegalArgumentException("serviceLocation cannot be null");
 | 
| 
 | 
   | 
146
 | 
+    }
 | 
| 
137
 | 
     this.serviceLocation = serviceLocation;
 | 
147
 | 
     this.serviceLocation = serviceLocation;
 | 
| 
 | 
   | 
148
 | 
+    try {
 | 
| 
 | 
   | 
149
 | 
+      serviceLocationURI = new URI(this.serviceLocation);
 | 
| 
 | 
   | 
150
 | 
+    } catch (URISyntaxException e) {
 | 
| 
 | 
   | 
151
 | 
+      throw new RuntimeException(e);
 | 
| 
 | 
   | 
152
 | 
+    }
 | 
| 
138
 | 
     this.httpClient = httpClient == null ? getDefaultHttpClient() : httpClient;
 | 
153
 | 
     this.httpClient = httpClient == null ? getDefaultHttpClient() : httpClient;
 | 
| 
139
 | 
   }
 | 
154
 | 
   }
 | 
| 
140
 | 
 
 | 
155
 | 
 
 | 
 | 
@@ -170,7 +185,7 @@ public class YoutrackAPI { | 
 | 
@@ -170,7 +185,7 @@ public class YoutrackAPI { | 
| 
170
 | 
 
 | 
185
 | 
 
 | 
| 
171
 | 
   public void command(String issueId, String command, String comment, String group, Boolean disableNotifications, String runAs) throws IOException
 | 
186
 | 
   public void command(String issueId, String command, String comment, String group, Boolean disableNotifications, String runAs) throws IOException
 | 
| 
172
 | 
   {
 | 
187
 | 
   {
 | 
| 
173
 | 
-    final HttpPost request = new HttpPost(serviceLocation + "/rest/issue/" + issueId + "/execute");
 | 
188
 | 
+    final HttpPost request = new HttpPost(buildURI(serviceLocationURI, "/rest/issue/" + issueId + "/execute"));
 | 
| 
174
 | 
     final List<BasicNameValuePair> parameters = new ArrayList<BasicNameValuePair>();
 | 
189
 | 
     final List<BasicNameValuePair> parameters = new ArrayList<BasicNameValuePair>();
 | 
| 
175
 | 
     parameters.add(new BasicNameValuePair("command", command));
 | 
190
 | 
     parameters.add(new BasicNameValuePair("command", command));
 | 
| 
176
 | 
     if (!isBlank(comment)) {
 | 
191
 | 
     if (!isBlank(comment)) {
 | 
 | 
@@ -202,14 +217,8 @@ public class YoutrackAPI { | 
 | 
@@ -202,14 +217,8 @@ public class YoutrackAPI { | 
| 
202
 | 
    */
 | 
217
 | 
    */
 | 
| 
203
 | 
   public String createIssue(String project, String summary, String description) throws IOException
 | 
218
 | 
   public String createIssue(String project, String summary, String description) throws IOException
 | 
| 
204
 | 
   {
 | 
219
 | 
   {
 | 
| 
205
 | 
-    final URI uri;
 | 
 | 
   | 
| 
206
 | 
-    try {
 | 
 | 
   | 
| 
207
 | 
-      uri = new URIBuilder(serviceLocation + "/rest/issue").build();
 | 
 | 
   | 
| 
208
 | 
-    } catch (URISyntaxException e) {
 | 
 | 
   | 
| 
209
 | 
-      throw new RuntimeException(e);
 | 
 | 
   | 
| 
210
 | 
-    }
 | 
 | 
   | 
| 
211
 | 
-    final HttpPut request = createPutRequest(uri, new BasicNameValuePair("project", project), new BasicNameValuePair("summary", summary),
 | 
 | 
   | 
| 
212
 | 
-        new BasicNameValuePair("description", description));
 | 
220
 | 
+    final HttpPut request = createPutRequest(buildURI(serviceLocationURI, "/rest/issue"), new BasicNameValuePair("project", project),
 | 
| 
 | 
   | 
221
 | 
+        new BasicNameValuePair("summary", summary), new BasicNameValuePair("description", description));
 | 
| 
213
 | 
     final HttpResponse response = httpClient.execute(request);
 | 
222
 | 
     final HttpResponse response = httpClient.execute(request);
 | 
| 
214
 | 
     final StatusLine statusLine = response.getStatusLine();
 | 
223
 | 
     final StatusLine statusLine = response.getStatusLine();
 | 
| 
215
 | 
     final HttpEntity entity = response.getEntity();
 | 
224
 | 
     final HttpEntity entity = response.getEntity();
 | 
 | 
@@ -230,24 +239,12 @@ public class YoutrackAPI { | 
 | 
@@ -230,24 +239,12 @@ public class YoutrackAPI { | 
| 
230
 | 
 
 | 
239
 | 
 
 | 
| 
231
 | 
   public void deleteIssue(String issueId) throws IOException
 | 
240
 | 
   public void deleteIssue(String issueId) throws IOException
 | 
| 
232
 | 
   {
 | 
241
 | 
   {
 | 
| 
233
 | 
-    final URI uri;
 | 
 | 
   | 
| 
234
 | 
-    try {
 | 
 | 
   | 
| 
235
 | 
-      uri = new URIBuilder(serviceLocation + "/rest/issue/" + issueId).build();
 | 
 | 
   | 
| 
236
 | 
-    } catch (URISyntaxException e) {
 | 
 | 
   | 
| 
237
 | 
-      throw new RuntimeException(e);
 | 
 | 
   | 
| 
238
 | 
-    }
 | 
 | 
   | 
| 
239
 | 
-    execute(new HttpDelete(uri));
 | 
242
 | 
+    execute(new HttpDelete(buildURI(serviceLocationURI, "/rest/issue/" + issueId)));
 | 
| 
240
 | 
   }
 | 
243
 | 
   }
 | 
| 
241
 | 
 
 | 
244
 | 
 
 | 
| 
242
 | 
   public AssigneeList getAssignees(String project) throws IOException, JAXBException
 | 
245
 | 
   public AssigneeList getAssignees(String project) throws IOException, JAXBException
 | 
| 
243
 | 
   {
 | 
246
 | 
   {
 | 
| 
244
 | 
-    final URI uri;
 | 
 | 
   | 
| 
245
 | 
-    try {
 | 
 | 
   | 
| 
246
 | 
-      uri = new URIBuilder(serviceLocation + "/rest/admin/project/" + project + "/assignee").build();
 | 
 | 
   | 
| 
247
 | 
-    } catch (URISyntaxException e) {
 | 
 | 
   | 
| 
248
 | 
-      throw new RuntimeException(e);
 | 
 | 
   | 
| 
249
 | 
-    }
 | 
 | 
   | 
| 
250
 | 
-    final String responseString = execute(new HttpGet(uri));
 | 
247
 | 
+    final String responseString = execute(new HttpGet(buildURI(serviceLocationURI, "/rest/admin/project/" + project + "/assignee")));
 | 
| 
251
 | 
     final Object result = YoutrackUnmarshaller.unmarshall(responseString);
 | 
248
 | 
     final Object result = YoutrackUnmarshaller.unmarshall(responseString);
 | 
| 
252
 | 
     if (result instanceof AssigneeList) {
 | 
249
 | 
     if (result instanceof AssigneeList) {
 | 
| 
253
 | 
       return (AssigneeList) result;
 | 
250
 | 
       return (AssigneeList) result;
 | 
 | 
@@ -258,13 +255,7 @@ public class YoutrackAPI { | 
 | 
@@ -258,13 +255,7 @@ public class YoutrackAPI { | 
| 
258
 | 
 
 | 
255
 | 
 
 | 
| 
259
 | 
   public Enumeration getBundle(String customField) throws IOException, JAXBException
 | 
256
 | 
   public Enumeration getBundle(String customField) throws IOException, JAXBException
 | 
| 
260
 | 
   {
 | 
257
 | 
   {
 | 
| 
261
 | 
-    final URI uri;
 | 
 | 
   | 
| 
262
 | 
-    try {
 | 
 | 
   | 
| 
263
 | 
-      uri = new URIBuilder(serviceLocation + "/rest/admin/customfield/bundle/" + customField).build();
 | 
 | 
   | 
| 
264
 | 
-    } catch (URISyntaxException e) {
 | 
 | 
   | 
| 
265
 | 
-      throw new RuntimeException(e);
 | 
 | 
   | 
| 
266
 | 
-    }
 | 
 | 
   | 
| 
267
 | 
-    final Object result = YoutrackUnmarshaller.unmarshall(execute(new HttpGet(uri)));
 | 
258
 | 
+    final Object result = YoutrackUnmarshaller.unmarshall(execute(new HttpGet(buildURI(serviceLocationURI, "/rest/admin/customfield/bundle/" + customField))));
 | 
| 
268
 | 
     if (result instanceof Enumeration) {
 | 
259
 | 
     if (result instanceof Enumeration) {
 | 
| 
269
 | 
       return (Enumeration) result;
 | 
260
 | 
       return (Enumeration) result;
 | 
| 
270
 | 
     } else if (result instanceof JAXBElement) {
 | 
261
 | 
     } else if (result instanceof JAXBElement) {
 | 
 | 
@@ -281,13 +272,7 @@ public class YoutrackAPI { | 
 | 
@@ -281,13 +272,7 @@ public class YoutrackAPI { | 
| 
281
 | 
 
 | 
272
 | 
 
 | 
| 
282
 | 
   public List<User> getIndividualAssignees(String project) throws IOException, JAXBException
 | 
273
 | 
   public List<User> getIndividualAssignees(String project) throws IOException, JAXBException
 | 
| 
283
 | 
   {
 | 
274
 | 
   {
 | 
| 
284
 | 
-    final URI uri;
 | 
 | 
   | 
| 
285
 | 
-    try {
 | 
 | 
   | 
| 
286
 | 
-      uri = new URIBuilder(serviceLocation + "/rest/admin/project/" + project + "/assignee/individual").build();
 | 
 | 
   | 
| 
287
 | 
-    } catch (URISyntaxException e) {
 | 
 | 
   | 
| 
288
 | 
-      throw new RuntimeException(e);
 | 
 | 
   | 
| 
289
 | 
-    }
 | 
 | 
   | 
| 
290
 | 
-    final String responseString = execute(new HttpGet(uri));
 | 
275
 | 
+    final String responseString = execute(new HttpGet(buildURI(serviceLocationURI, "/rest/admin/project/" + project + "/assignee/individual")));
 | 
| 
291
 | 
     final Object result = YoutrackUnmarshaller.unmarshall(responseString);
 | 
276
 | 
     final Object result = YoutrackUnmarshaller.unmarshall(responseString);
 | 
| 
292
 | 
     if (result instanceof UserRefs) {
 | 
277
 | 
     if (result instanceof UserRefs) {
 | 
| 
293
 | 
       return ((UserRefs) result).getUsers();
 | 
278
 | 
       return ((UserRefs) result).getUsers();
 | 
 | 
@@ -298,15 +283,9 @@ public class YoutrackAPI { | 
 | 
@@ -298,15 +283,9 @@ public class YoutrackAPI { | 
| 
298
 | 
 
 | 
283
 | 
 
 | 
| 
299
 | 
   public IssueWrapper getIssue(String issueId) throws IOException, JAXBException
 | 
284
 | 
   public IssueWrapper getIssue(String issueId) throws IOException, JAXBException
 | 
| 
300
 | 
   {
 | 
285
 | 
   {
 | 
| 
301
 | 
-    final URI uri;
 | 
 | 
   | 
| 
302
 | 
-    try {
 | 
 | 
   | 
| 
303
 | 
-      uri = new URIBuilder(serviceLocation + "/rest/issue/" + issueId).build();
 | 
 | 
   | 
| 
304
 | 
-    } catch (URISyntaxException e) {
 | 
 | 
   | 
| 
305
 | 
-      throw new RuntimeException(e);
 | 
 | 
   | 
| 
306
 | 
-    }
 | 
 | 
   | 
| 
307
 | 
     final String responseString;
 | 
286
 | 
     final String responseString;
 | 
| 
308
 | 
     try {
 | 
287
 | 
     try {
 | 
| 
309
 | 
-      responseString = execute(new HttpGet(uri));
 | 
288
 | 
+      responseString = execute(new HttpGet(buildURI(serviceLocationURI, "/rest/issue/" + issueId)));
 | 
| 
310
 | 
     } catch (YoutrackErrorException e) {
 | 
289
 | 
     } catch (YoutrackErrorException e) {
 | 
| 
311
 | 
       if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
 | 
290
 | 
       if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
 | 
| 
312
 | 
         throw new NoResultFoundException(e.getMessage(), e);
 | 
291
 | 
         throw new NoResultFoundException(e.getMessage(), e);
 | 
 | 
@@ -331,15 +310,15 @@ public class YoutrackAPI { | 
 | 
@@ -331,15 +310,15 @@ public class YoutrackAPI { | 
| 
331
 | 
 
 | 
310
 | 
 
 | 
| 
332
 | 
   public void login(String username, String password) throws IOException, JAXBException
 | 
311
 | 
   public void login(String username, String password) throws IOException, JAXBException
 | 
| 
333
 | 
   {
 | 
312
 | 
   {
 | 
| 
334
 | 
-    final HttpPost request = new HttpPost(serviceLocation + "/rest/user/login");
 | 
313
 | 
+    final HttpPost request = new HttpPost(buildURI(serviceLocationURI, "/rest/user/login"));
 | 
| 
335
 | 
     request.setEntity(new UrlEncodedFormEntity(Arrays.asList(new BasicNameValuePair("login", username), new BasicNameValuePair("password", password))));
 | 
314
 | 
     request.setEntity(new UrlEncodedFormEntity(Arrays.asList(new BasicNameValuePair("login", username), new BasicNameValuePair("password", password))));
 | 
| 
336
 | 
     execute(request);
 | 
315
 | 
     execute(request);
 | 
| 
337
 | 
   }
 | 
316
 | 
   }
 | 
| 
338
 | 
 
 | 
317
 | 
 
 | 
| 
339
 | 
   public List<IssueWrapper> searchIssuesByProject(String project, Object filter) throws JAXBException, IOException
 | 
318
 | 
   public List<IssueWrapper> searchIssuesByProject(String project, Object filter) throws JAXBException, IOException
 | 
| 
340
 | 
   {
 | 
319
 | 
   {
 | 
| 
341
 | 
-    final String url = serviceLocation + "/rest/issue/byproject/" + project + "?filter=" + (filter == null ? "" : filter);
 | 
 | 
   | 
| 
342
 | 
-    final Object result = YoutrackUnmarshaller.unmarshall(execute(new HttpGet(url)));
 | 
320
 | 
+    final Object result = YoutrackUnmarshaller.unmarshall(
 | 
| 
 | 
   | 
321
 | 
+        execute(new HttpGet(buildURI(serviceLocationURI, "/rest/issue/byproject/" + project, "filter=" + (filter == null ? "" : filter)))));
 | 
| 
343
 | 
     if (!(result instanceof Issues)) {
 | 
322
 | 
     if (!(result instanceof Issues)) {
 | 
| 
344
 | 
       throw new YoutrackAPIException("Unmarshalling problem. Expected Issues, received: " + result.getClass() + " " + result);
 | 
323
 | 
       throw new YoutrackAPIException("Unmarshalling problem. Expected Issues, received: " + result.getClass() + " " + result);
 | 
| 
345
 | 
     }
 | 
324
 | 
     }
 | 
 | 
@@ -353,13 +332,7 @@ public class YoutrackAPI { | 
 | 
@@ -353,13 +332,7 @@ public class YoutrackAPI { | 
| 
353
 | 
 
 | 
332
 | 
 
 | 
| 
354
 | 
   public void updateIssue(String issueId, String summary, String description) throws IOException
 | 
333
 | 
   public void updateIssue(String issueId, String summary, String description) throws IOException
 | 
| 
355
 | 
   {
 | 
334
 | 
   {
 | 
| 
356
 | 
-    final URI uri;
 | 
 | 
   | 
| 
357
 | 
-    try {
 | 
 | 
   | 
| 
358
 | 
-      uri = new URIBuilder(serviceLocation + "/rest/issue/" + issueId).build();
 | 
 | 
   | 
| 
359
 | 
-    } catch (URISyntaxException e) {
 | 
 | 
   | 
| 
360
 | 
-      throw new RuntimeException(e);
 | 
 | 
   | 
| 
361
 | 
-    }
 | 
 | 
   | 
| 
362
 | 
-    final HttpPost request = createPostRequest(uri, new BasicNameValuePair(Fields.summary.name(), summary),
 | 
335
 | 
+    final HttpPost request = createPostRequest(buildURI(serviceLocationURI, "/rest/issue/" + issueId), new BasicNameValuePair(Fields.summary.name(), summary),
 | 
| 
363
 | 
         new BasicNameValuePair(Fields.description.name(), description));
 | 
336
 | 
         new BasicNameValuePair(Fields.description.name(), description));
 | 
| 
364
 | 
     final HttpResponse response = httpClient.execute(request);
 | 
337
 | 
     final HttpResponse response = httpClient.execute(request);
 | 
| 
365
 | 
     final StatusLine statusLine = response.getStatusLine();
 | 
338
 | 
     final StatusLine statusLine = response.getStatusLine();
 | 
 | 
@@ -389,7 +362,7 @@ public class YoutrackAPI { | 
 | 
@@ -389,7 +362,7 @@ public class YoutrackAPI { | 
| 
389
 | 
         final String error = ErrorUnmarshaller.unmarshal(responseText);
 | 
362
 | 
         final String error = ErrorUnmarshaller.unmarshal(responseText);
 | 
| 
390
 | 
         throw new YoutrackErrorException(error, statusLine.getStatusCode());
 | 
363
 | 
         throw new YoutrackErrorException(error, statusLine.getStatusCode());
 | 
| 
391
 | 
       } catch (JAXBException e) {
 | 
364
 | 
       } catch (JAXBException e) {
 | 
| 
392
 | 
-        //                    TODO we should log on debug level the JAXBException
 | 
365
 | 
+        LOG.error("Cannot unmarshal following response text:\n" + responseText, e);
 | 
| 
393
 | 
         throw new HttpResponseException(statusLine.getStatusCode(), responseText);
 | 
366
 | 
         throw new HttpResponseException(statusLine.getStatusCode(), responseText);
 | 
| 
394
 | 
       }
 | 
367
 | 
       }
 | 
| 
395
 | 
     }
 | 
368
 | 
     }
 |