bernard
authored
2013-04-04 13:08:24 +0000
1
package pl . itcrowd . youtrack . api ;
bernard
authored
2011-12-05 08:00:02 +0000
2
bernard
authored
2012-11-05 09:56:36 +0000
3
4
import org.apache.commons.logging.Log ;
import org.apache.commons.logging.LogFactory ;
bernard
authored
2012-07-02 21:19:19 +0000
5
6
7
8
9
import org.apache.http.Header ;
import org.apache.http.HttpEntity ;
import org.apache.http.HttpHeaders ;
import org.apache.http.HttpResponse ;
import org.apache.http.HttpStatus ;
bernard
authored
2012-10-31 14:56:42 +0000
10
import org.apache.http.NameValuePair ;
bernard
authored
2012-07-02 21:19:19 +0000
11
12
13
14
15
import org.apache.http.StatusLine ;
import org.apache.http.client.ClientProtocolException ;
import org.apache.http.client.HttpClient ;
import org.apache.http.client.HttpResponseException ;
import org.apache.http.client.entity.UrlEncodedFormEntity ;
bernard
authored
2012-11-04 20:40:52 +0000
16
import org.apache.http.client.methods.HttpDelete ;
bernard
authored
2012-10-31 14:56:42 +0000
17
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase ;
bernard
authored
2012-07-02 21:19:19 +0000
18
19
20
21
22
23
24
25
26
27
28
29
30
import org.apache.http.client.methods.HttpGet ;
import org.apache.http.client.methods.HttpPost ;
import org.apache.http.client.methods.HttpPut ;
import org.apache.http.client.methods.HttpUriRequest ;
import org.apache.http.conn.ClientConnectionManager ;
import org.apache.http.conn.scheme.Scheme ;
import org.apache.http.conn.scheme.SchemeRegistry ;
import org.apache.http.conn.ssl.SSLSocketFactory ;
import org.apache.http.impl.client.DefaultHttpClient ;
import org.apache.http.impl.conn.PoolingClientConnectionManager ;
import org.apache.http.impl.conn.SchemeRegistryFactory ;
import org.apache.http.message.BasicNameValuePair ;
import org.apache.http.util.EntityUtils ;
bernard
authored
2013-04-04 13:08:24 +0000
31
32
33
34
35
36
37
38
39
40
import pl.itcrowd.youtrack.api.defaults.Fields ;
import pl.itcrowd.youtrack.api.exceptions.NoResultFoundException ;
import pl.itcrowd.youtrack.api.exceptions.YoutrackAPIException ;
import pl.itcrowd.youtrack.api.exceptions.YoutrackErrorException ;
import pl.itcrowd.youtrack.api.rest.AssigneeList ;
import pl.itcrowd.youtrack.api.rest.Enumeration ;
import pl.itcrowd.youtrack.api.rest.Issue ;
import pl.itcrowd.youtrack.api.rest.Issues ;
import pl.itcrowd.youtrack.api.rest.User ;
import pl.itcrowd.youtrack.api.rest.UserRefs ;
bernard
authored
2011-12-05 08:00:02 +0000
41
bernard
authored
2012-07-02 21:19:19 +0000
42
43
44
45
import javax.net.ssl.SSLContext ;
import javax.net.ssl.TrustManager ;
import javax.net.ssl.X509TrustManager ;
import javax.xml.bind.JAXBElement ;
bernard
authored
2011-12-05 08:00:02 +0000
46
import javax.xml.bind.JAXBException ;
bernard
authored
2012-07-03 10:50:59 +0000
47
import javax.xml.namespace.QName ;
bernard
authored
2011-12-05 08:00:02 +0000
48
import java.io.IOException ;
bernard
authored
2012-10-30 10:07:05 +0000
49
import java.io.UnsupportedEncodingException ;
bernard
authored
2012-07-02 21:19:19 +0000
50
51
52
53
54
55
56
import java.net.URI ;
import java.net.URISyntaxException ;
import java.security.KeyManagementException ;
import java.security.NoSuchAlgorithmException ;
import java.security.SecureRandom ;
import java.security.cert.CertificateException ;
import java.security.cert.X509Certificate ;
bernard
authored
2011-12-05 08:00:02 +0000
57
import java.util.ArrayList ;
bernard
authored
2012-07-02 21:19:19 +0000
58
import java.util.Arrays ;
bernard
authored
2012-10-31 14:56:42 +0000
59
import java.util.Collections ;
bernard
authored
2011-12-16 08:51:04 +0000
60
import java.util.List ;
bernard
authored
2012-07-02 21:19:19 +0000
61
62
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
bernard
authored
2011-12-05 08:00:02 +0000
63
bernard
authored
2013-04-04 13:08:24 +0000
64
import static pl . itcrowd . youtrack . api . URIUtils . buildURI ;
bernard
authored
2012-11-05 09:56:36 +0000
65
bernard
authored
2011-12-05 08:00:02 +0000
66
public class YoutrackAPI {
bernard
authored
2011-12-16 08:51:04 +0000
67
// ------------------------------ FIELDS ------------------------------
bernard
authored
2011-12-05 08:00:02 +0000
68
bernard
authored
2012-11-04 20:40:52 +0000
69
private final static QName Enumeration_QNAME = new QName ( "" , "enumeration" );
bernard
authored
2012-10-31 16:56:49 +0000
70
bernard
authored
2012-11-04 20:40:52 +0000
71
private final static QName Issue_QNAME = new QName ( "" , "issue" );
bernard
authored
2012-07-03 10:50:59 +0000
72
bernard
authored
2012-11-05 09:56:36 +0000
73
74
private static Log LOG = LogFactory . getLog ( YoutrackAPI . class );
bernard
authored
2012-11-04 20:40:52 +0000
75
private HttpClient httpClient ;
bernard
authored
2012-07-02 21:19:19 +0000
76
bernard
authored
2012-11-04 20:40:52 +0000
77
private String serviceLocation ;
bernard
authored
2011-12-05 08:00:02 +0000
78
bernard
authored
2012-11-05 09:56:36 +0000
79
80
private URI serviceLocationURI ;
bernard
authored
2012-07-02 21:19:19 +0000
81
82
// -------------------------- STATIC METHODS --------------------------
bernard
authored
2012-11-04 20:40:52 +0000
83
84
85
86
87
88
89
90
91
92
93
94
95
private static HttpClient getDefaultHttpClient ()
{
SSLContext sslContext ;
try {
sslContext = SSLContext . getInstance ( "SSL" );
} catch ( NoSuchAlgorithmException e ) {
throw new RuntimeException ( e );
}
// set up a TrustManager that trusts everything
try {
sslContext . init ( null , new TrustManager []{ new X509TrustManager () {
public void checkClientTrusted ( X509Certificate [] x509Certificates , String s ) throws CertificateException
{
bernard
authored
2012-07-02 21:19:19 +0000
96
97
}
bernard
authored
2012-11-04 20:40:52 +0000
98
99
100
public void checkServerTrusted ( X509Certificate [] x509Certificates , String s ) throws CertificateException
{
}
bernard
authored
2012-07-02 21:19:19 +0000
101
bernard
authored
2012-11-04 20:40:52 +0000
102
103
104
105
106
107
108
public X509Certificate [] getAcceptedIssuers ()
{
return new X509Certificate [ 0 ];
}
}}, new SecureRandom ());
} catch ( KeyManagementException e ) {
throw new RuntimeException ( e );
bernard
authored
2012-07-02 21:19:19 +0000
109
}
bernard
authored
2011-12-05 08:00:02 +0000
110
bernard
authored
2012-11-04 20:40:52 +0000
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
SSLSocketFactory sf = new SSLSocketFactory ( sslContext );
Scheme httpsScheme = new Scheme ( "https" , 443 , sf );
SchemeRegistry schemeRegistry = SchemeRegistryFactory . createDefault ();
schemeRegistry . register ( schemeRegistry . unregister ( "https" ));
schemeRegistry . register ( httpsScheme );
ClientConnectionManager cm = new PoolingClientConnectionManager ( schemeRegistry );
return new DefaultHttpClient ( cm );
}
private static boolean isBlank ( String str )
{
int strLen ;
if ( str == null || ( strLen = str . length ()) == 0 ) {
return true ;
}
for ( int i = 0 ; i < strLen ; i ++) {
if ((! Character . isWhitespace ( str . charAt ( i )))) {
return false ;
}
bernard
authored
2012-07-03 10:19:33 +0000
131
}
bernard
authored
2012-11-04 20:40:52 +0000
132
133
return true ;
}
bernard
authored
2012-07-03 10:19:33 +0000
134
bernard
authored
2011-12-16 08:51:04 +0000
135
// --------------------------- CONSTRUCTORS ---------------------------
bernard
authored
2011-12-05 08:00:02 +0000
136
bernard
authored
2012-11-04 20:40:52 +0000
137
138
139
140
public YoutrackAPI ( String serviceLocation )
{
this ( serviceLocation , null );
}
bernard
authored
2012-07-02 21:19:19 +0000
141
bernard
authored
2012-11-04 20:40:52 +0000
142
143
public YoutrackAPI ( String serviceLocation , HttpClient httpClient )
{
bernard
authored
2012-11-05 09:56:36 +0000
144
145
146
if ( serviceLocation == null ) {
throw new IllegalArgumentException ( "serviceLocation cannot be null" );
}
bernard
authored
2012-11-04 20:40:52 +0000
147
this . serviceLocation = serviceLocation ;
bernard
authored
2012-11-05 09:56:36 +0000
148
149
150
151
152
try {
serviceLocationURI = new URI ( this . serviceLocation );
} catch ( URISyntaxException e ) {
throw new RuntimeException ( e );
}
bernard
authored
2012-11-04 20:40:52 +0000
153
154
this . httpClient = httpClient == null ? getDefaultHttpClient () : httpClient ;
}
bernard
authored
2011-12-05 08:00:02 +0000
155
bernard
authored
2012-11-04 20:40:52 +0000
156
157
158
159
public YoutrackAPI ( String serviceLocation , String username , String password ) throws IOException , JAXBException
{
this ( serviceLocation , null , username , password );
}
bernard
authored
2012-10-31 14:56:42 +0000
160
bernard
authored
2012-11-04 20:40:52 +0000
161
162
163
164
165
public YoutrackAPI ( String serviceLocation , HttpClient httpClient , String username , String password ) throws IOException , JAXBException
{
this ( serviceLocation , httpClient );
login ( username , password );
}
bernard
authored
2011-12-05 08:00:02 +0000
166
bernard
authored
2011-12-16 08:51:04 +0000
167
168
// --------------------- GETTER / SETTER METHODS ---------------------
bernard
authored
2012-11-04 20:40:52 +0000
169
170
171
172
public String getServiceLocation ()
{
return serviceLocation ;
}
bernard
authored
2011-12-16 08:51:04 +0000
173
174
175
// -------------------------- OTHER METHODS --------------------------
bernard
authored
2012-11-04 20:40:52 +0000
176
177
178
179
180
181
182
183
184
185
186
187
public void command ( String issueId , String command ) throws IOException
{
command ( issueId , command , null , null , null , null );
}
public void command ( String issueId , Command command ) throws IOException
{
command ( issueId , command . toString ());
}
public void command ( String issueId , String command , String comment , String group , Boolean disableNotifications , String runAs ) throws IOException
{
bernard
authored
2012-11-05 09:56:36 +0000
188
final HttpPost request = new HttpPost ( buildURI ( serviceLocationURI , "/rest/issue/" + issueId + "/execute" ));
bernard
authored
2012-11-04 20:40:52 +0000
189
190
191
192
final List < BasicNameValuePair > parameters = new ArrayList < BasicNameValuePair >();
parameters . add ( new BasicNameValuePair ( "command" , command ));
if (! isBlank ( comment )) {
parameters . add ( new BasicNameValuePair ( "comment" , comment ));
bernard
authored
2012-07-03 10:19:33 +0000
193
}
bernard
authored
2012-11-04 20:40:52 +0000
194
195
if (! isBlank ( group )) {
parameters . add ( new BasicNameValuePair ( "group" , group ));
bernard
authored
2012-07-03 10:19:33 +0000
196
}
bernard
authored
2012-11-04 20:40:52 +0000
197
198
if ( disableNotifications != null ) {
parameters . add ( new BasicNameValuePair ( "disableNotifications" , disableNotifications . toString ()));
bernard
authored
2012-07-03 10:19:33 +0000
199
}
bernard
authored
2012-11-04 20:40:52 +0000
200
201
if (! isBlank ( runAs )) {
parameters . add ( new BasicNameValuePair ( "runAs" , runAs ));
bernard
authored
2012-07-02 21:19:19 +0000
202
}
bernard
authored
2012-11-04 20:40:52 +0000
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
request . setEntity ( new UrlEncodedFormEntity ( parameters ));
execute ( request );
}
/**
* Creates new issue on Youtrack.
*
* @param project project to create issue in
* @param summary summary of the issue
* @param description longer description of the issue
*
* @return issue id of created issue
*
* @throws IOException in case of communication problems
*/
public String createIssue ( String project , String summary , String description ) throws IOException
{
bernard
authored
2012-11-05 09:56:36 +0000
220
221
final HttpPut request = createPutRequest ( buildURI ( serviceLocationURI , "/rest/issue" ), new BasicNameValuePair ( "project" , project ),
new BasicNameValuePair ( "summary" , summary ), new BasicNameValuePair ( "description" , description ));
bernard
authored
2012-11-04 20:40:52 +0000
222
223
224
225
226
227
228
229
230
final HttpResponse response = httpClient . execute ( request );
final StatusLine statusLine = response . getStatusLine ();
final HttpEntity entity = response . getEntity ();
final String responseText = entity == null ? null : EntityUtils . toString ( entity );
throwExceptionsIfNeeded ( statusLine , responseText );
final Header header = response . getFirstHeader ( HttpHeaders . LOCATION );
if ( header == null ) {
throw new YoutrackAPIException ( "Missing location header despite positive status code: " + statusLine . getStatusCode ());
bernard
authored
2012-07-05 16:25:04 +0000
231
}
bernard
authored
2012-11-04 20:40:52 +0000
232
233
234
235
final String issueURL = header . getValue ();
final Matcher matcher = Pattern . compile ( ".*(" + project + "-\\d+)" ). matcher ( issueURL );
if (! matcher . find () || matcher . groupCount () < 1 ) {
throw new YoutrackAPIException ( "Cannot extract issue id from issue URL: " + issueURL );
bernard
authored
2012-07-03 10:50:59 +0000
236
}
bernard
authored
2012-11-04 20:40:52 +0000
237
238
239
240
241
return matcher . group ( 1 );
}
public void deleteIssue ( String issueId ) throws IOException
{
bernard
authored
2012-11-05 09:56:36 +0000
242
execute ( new HttpDelete ( buildURI ( serviceLocationURI , "/rest/issue/" + issueId )));
bernard
authored
2012-11-04 20:40:52 +0000
243
244
245
246
}
public AssigneeList getAssignees ( String project ) throws IOException , JAXBException
{
bernard
authored
2012-11-05 09:56:36 +0000
247
final String responseString = execute ( new HttpGet ( buildURI ( serviceLocationURI , "/rest/admin/project/" + project + "/assignee" )));
bernard
authored
2012-11-04 20:40:52 +0000
248
249
250
251
252
final Object result = YoutrackUnmarshaller . unmarshall ( responseString );
if ( result instanceof AssigneeList ) {
return ( AssigneeList ) result ;
} else {
throw new YoutrackAPIException ( "Unexpected type: " + result );
bernard
authored
2011-12-05 08:00:02 +0000
253
}
bernard
authored
2012-11-04 20:40:52 +0000
254
255
256
257
}
public Enumeration getBundle ( String customField ) throws IOException , JAXBException
{
bernard
authored
2012-11-05 09:56:36 +0000
258
final Object result = YoutrackUnmarshaller . unmarshall ( execute ( new HttpGet ( buildURI ( serviceLocationURI , "/rest/admin/customfield/bundle/" + customField ))));
bernard
authored
2012-11-04 20:40:52 +0000
259
260
261
262
263
264
265
266
267
268
269
if ( result instanceof Enumeration ) {
return ( Enumeration ) result ;
} else if ( result instanceof JAXBElement ) {
final JAXBElement jaxbElement = ( JAXBElement ) result ;
if ( Enumeration_QNAME . equals ( jaxbElement . getName ())) {
return ( Enumeration ) (( JAXBElement ) result ). getValue ();
} else {
throw new YoutrackAPIException ( "Unexpected type: " + jaxbElement . getValue ());
}
} else {
throw new YoutrackAPIException ( "Unexpected type: " + result );
bernard
authored
2012-10-31 14:56:42 +0000
270
}
bernard
authored
2012-11-04 20:40:52 +0000
271
272
273
274
}
public List < User > getIndividualAssignees ( String project ) throws IOException , JAXBException
{
bernard
authored
2012-11-05 09:56:36 +0000
275
final String responseString = execute ( new HttpGet ( buildURI ( serviceLocationURI , "/rest/admin/project/" + project + "/assignee/individual" )));
bernard
authored
2012-11-04 20:40:52 +0000
276
277
278
279
280
final Object result = YoutrackUnmarshaller . unmarshall ( responseString );
if ( result instanceof UserRefs ) {
return (( UserRefs ) result ). getUsers ();
} else {
throw new YoutrackAPIException ( "Unexpected type: " + result );
bernard
authored
2012-07-02 21:19:19 +0000
281
}
bernard
authored
2012-11-04 20:40:52 +0000
282
283
284
285
286
287
}
public IssueWrapper getIssue ( String issueId ) throws IOException , JAXBException
{
final String responseString ;
try {
bernard
authored
2012-11-05 09:56:36 +0000
288
responseString = execute ( new HttpGet ( buildURI ( serviceLocationURI , "/rest/issue/" + issueId )));
bernard
authored
2012-11-04 20:40:52 +0000
289
290
291
292
293
294
295
296
} catch ( YoutrackErrorException e ) {
if ( e . getStatusCode () == HttpStatus . SC_NOT_FOUND ) {
throw new NoResultFoundException ( e . getMessage (), e );
} else {
throw e ;
}
}
final Object result = YoutrackUnmarshaller . unmarshall ( responseString );
bernard
authored
2013-04-04 13:08:24 +0000
297
if ( result instanceof pl . itcrowd . youtrack . api . rest . Issue ) {
bernard
authored
2012-11-04 20:40:52 +0000
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
return new IssueWrapper (( Issue ) result );
} else if ( result instanceof JAXBElement ) {
final JAXBElement jaxbElement = ( JAXBElement ) result ;
if ( Issue_QNAME . equals ( jaxbElement . getName ())) {
return new IssueWrapper (( Issue ) jaxbElement . getValue ());
} else {
throw new YoutrackAPIException ( "Unexpected type: " + jaxbElement . getValue ());
}
} else {
throw new YoutrackAPIException ( "Unexpected type " + result );
}
}
public void login ( String username , String password ) throws IOException , JAXBException
{
bernard
authored
2012-11-05 09:56:36 +0000
313
final HttpPost request = new HttpPost ( buildURI ( serviceLocationURI , "/rest/user/login" ));
bernard
authored
2012-11-04 20:40:52 +0000
314
315
316
317
318
319
request . setEntity ( new UrlEncodedFormEntity ( Arrays . asList ( new BasicNameValuePair ( "login" , username ), new BasicNameValuePair ( "password" , password ))));
execute ( request );
}
public List < IssueWrapper > searchIssuesByProject ( String project , Object filter ) throws JAXBException , IOException
{
bernard
authored
2012-11-05 09:56:36 +0000
320
321
final Object result = YoutrackUnmarshaller . unmarshall (
execute ( new HttpGet ( buildURI ( serviceLocationURI , "/rest/issue/byproject/" + project , "filter=" + ( filter == null ? "" : filter )))));
bernard
authored
2012-11-04 20:40:52 +0000
322
323
324
325
326
327
328
329
330
331
332
333
334
if (!( result instanceof Issues )) {
throw new YoutrackAPIException ( "Unmarshalling problem. Expected Issues, received: " + result . getClass () + " " + result );
}
List < Issue > issues = (( Issues ) result ). getIssues ();
List < IssueWrapper > wrappedIssues = new ArrayList < IssueWrapper >();
for ( Issue issue : issues ) {
wrappedIssues . add ( new IssueWrapper ( issue ));
}
return wrappedIssues ;
}
public void updateIssue ( String issueId , String summary , String description ) throws IOException
{
bernard
authored
2012-11-05 09:56:36 +0000
335
final HttpPost request = createPostRequest ( buildURI ( serviceLocationURI , "/rest/issue/" + issueId ), new BasicNameValuePair ( Fields . summary . name (), summary ),
bernard
authored
2012-11-04 20:40:52 +0000
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
new BasicNameValuePair ( Fields . description . name (), description ));
final HttpResponse response = httpClient . execute ( request );
final StatusLine statusLine = response . getStatusLine ();
final HttpEntity entity = response . getEntity ();
final String responseText = entity == null ? null : EntityUtils . toString ( entity );
throwExceptionsIfNeeded ( statusLine , responseText );
}
private HttpPost createPostRequest ( URI uri , NameValuePair ... nameValuePair ) throws UnsupportedEncodingException
{
return setEntity ( new HttpPost ( uri ), nameValuePair );
}
private HttpPut createPutRequest ( URI uri , NameValuePair ... nameValuePair ) throws UnsupportedEncodingException
{
return setEntity ( new HttpPut ( uri ), nameValuePair );
}
private String execute ( HttpUriRequest request ) throws IOException
{
final HttpResponse response = httpClient . execute ( request );
final StatusLine statusLine = response . getStatusLine ();
final HttpEntity entity = response . getEntity ();
String responseText = entity == null ? null : EntityUtils . toString ( entity );
if ( statusLine . getStatusCode () >= 300 ) {
try {
final String error = ErrorUnmarshaller . unmarshal ( responseText );
throw new YoutrackErrorException ( error , statusLine . getStatusCode ());
} catch ( JAXBException e ) {
bernard
authored
2012-11-05 09:56:36 +0000
365
LOG . error ( "Cannot unmarshal following response text:\n" + responseText , e );
bernard
authored
2012-11-04 20:40:52 +0000
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
throw new HttpResponseException ( statusLine . getStatusCode (), responseText );
}
}
if ( entity == null ) {
throw new ClientProtocolException ( "Response contains no content" );
}
return responseText ;
}
private < T extends HttpEntityEnclosingRequestBase > T setEntity ( T request , NameValuePair [] nameValuePair ) throws UnsupportedEncodingException
{
final ArrayList < NameValuePair > list = new ArrayList < NameValuePair >();
Collections . addAll ( list , nameValuePair );
request . setEntity ( new UrlEncodedFormEntity ( list ));
return request ;
}
private void throwExceptionsIfNeeded ( StatusLine statusLine , String responseText ) throws IOException
{
if ( statusLine . getStatusCode () >= 300 ) {
throw new HttpResponseException ( statusLine . getStatusCode (), responseText );
387
}
bernard
authored
2012-11-04 20:40:52 +0000
388
}
389
}