J Tricks - Little JIRA Tricks
 
Let us face it! REST is the order of the day. And Atlassian just underlined that statement with the release of JIRA5.

With JIRA5, a lot of the operations can be done using the REST API and here is the full list of operations supported.
There are so many ways to invoke the rest APIs. The Atlassian developer documentation is a good start. It gives you a good explanation on the input parameters needed for the various operations, if not fully clear from the API documentation.

But if you are yet unsure on how to invoke these REST operations from a Java client, read on...

Before boring you further, I must say that the best Java client for JIRA REST APIs is JRJC - The JIRA Rest Java Client supported by Atlassian. But at least for now, it doesn't support all the Issue CRUD methods. I must add that it won't be long before they are added (or is available when you re reading it!).

Still, what if you want to create another client? Without the dependency on something like JRJC? Let us look at a rather simple option - Using Jersey to write a REST Client.

I have mentioned about JRJC in my book but the following part is probably missing from the book. Considering the JIRA5 REST capabilities, it is never too late to write something about it, is it?

As mentioned before, I am just going to look at Jersey and see how we can write a simple client. Let us start with creating a simple maven project. Following is the dependency that needs to be added for using the jersey client libraries.

<dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-client</artifactId>
        <version>1.8</version>
</dependency>

Change the version as appropriate!

Consider a simple scenario. We need to get a list of all projects in a JIRA instance. As per the API docs, the REST url is /rest/api/2/project and it is a GET operation. Following are the steps:

  1. Choose the authentication style. We are going to use the basic authentication as that is the simplest to explain. But if you are interested, there is an explanation of OAuth dance here.
  2. Create the basic authentication String. We will be using it in the REST call.

    String auth = new String(Base64.encode("username:password"));

  3. Instantiate a Jersey Client and create a Web resource that is capable of building requests to send to the REST resource and process responses returned from the resource.

    Client client = Client.create();
    WebResource webResource = client.resource("http://localhost:8080/rest/api/2/project");

    Here the url used is the base url of JIRA + the rest resource url.

  4. Obtain the response using the webResource after setting the required parameters like authentication type, media type etc.

    ClientResponse response = webResource.header("Authorization", "Basic " + auth).type("application/json").accept("application/json").get(ClientResponse.class);

                
As you can see, we have set the basic authentication parameters in the header which includes the authentication String we created in Step 2. You can add even custom values in the header as long as the server can use it but in our case that is not required.

The media type we used is application/json as that is the one supported by JIRA REST methods.

We are also using the get method here and accepts the response into the ClientResponse class.

That's it! We have sent the request and got the response. It is now only a matter of making sure the response is what is expected. The easiest thing to do is to check the status code in the response. For example, a response code in the 200 range is normally successful where as 401, for example, indicates invalid authentication error!

A good list of status codes can be found here.

You can get the status code and check it like this:

int statusCode = response.getStatus();
if (statusCode == 401) {
    throw new AuthenticationException("Invalid Username or Password");
}

And what is the status code is fine? If you are expecting a response, like in our case - list of projects, we can get the result as shown:

String response = response.getEntity(String.class);

Here the response will be in json format since that is what is given back by JIRA. Remember, the media type we set was application/json!

A sample output is here:

[
    {
        "self": "http://localhost:8080/rest/api/2/project/DEMO",
        "id": "10000",
        "key": "DEMO",
        "name": "DEMO",
        "avatarUrls": {
            "16x16": "http://localhost:8080/secure/projectavatar?size=small&pid=10000&avatarId=10011",
            "48x48": "http://localhost:8080/secure/projectavatar?pid=10000&avatarId=10011"
        }
    },
    {
        "self": "http://localhost:8080/rest/api/2/project/TEST",
        "id": "10001",
        "key": "TEST",
        "name": "TEST",
        "avatarUrls": {
            "16x16": "http://localhost:8080/secure/projectavatar?size=small&pid=10001&avatarId=10011",
            "48x48": "http://localhost:8080/secure/projectavatar?pid=10001&avatarId=10011"
        }
    }
]

As you can see, the response here is a JSON array and you can now extract the details from here as you wish! Here is small example of extracting the key and name.

We will use JSON in java for this. The dependency to be added is:

<dependency>
        <groupId>org.json</groupId>
        <artifactId>json</artifactId>
        <version>20090211</version>
</dependency>

You can simply extract the key and name as follows:

JSONArray projectArray = new JSONArray(jsonResponse);
for (int i = 0; i < projectArray.length(); i++) {
    JSONObject proj = projectArray.getJSONObject(i);
    System.out.println("Key:"+proj.getString("key")+", Name:"+proj.getString("name"));
}

So, that was a simple GET example. How about creating/updating/deleting an issue? I have taken these examples because they use POST/PUT/DELETE operations.

Creating an issue

As per the docs, we need to POST the fields required to create the issue to the REST resource. The url is http://localhost:8080/rest/api/2/issue and the simple json input with just summary, project and issuetype details will be something like this:

{"fields":{"project":{"key":"DEMO"},"summary":"REST Test","issuetype":{"name":"Bug"}}}

Everything else remains the same expect that we will be using post method with the above data.

private static String invokePostMethod(String auth, String url, String data) throws AuthenticationException, ClientHandlerException {
    Client client = Client.create();
    WebResource webResource = client.resource(url);
    ClientResponse response = webResource.header("Authorization", "Basic " + auth).type("application/json")
                .accept("application/json").post(ClientResponse.class, data);
    int statusCode = response.getStatus();
    if (statusCode == 401) {
        throw new AuthenticationException("Invalid Username or Password");
    }
    return response.getEntity(String.class);
}

Updating an issue

The url will have the issue key we need to edit: http://localhost:8080/rest/api/2/issue/DEMO-13. A simple example of json data to change the assignee will be:

{"fields":{"assignee":{"name":"test"}}}

Everything else remains same except that we use the PUT method:

private static void invokePutMethod(String auth, String url, String data) throws AuthenticationException, ClientHandlerException {
    Client client = Client.create();
    WebResource webResource = client.resource(url);
    ClientResponse response = webResource.header("Authorization", "Basic " + auth).type("application/json")
                .accept("application/json").put(ClientResponse.class, data);
    int statusCode = response.getStatus();
    if (statusCode == 401) {
        throw new AuthenticationException("Invalid Username or Password");
    }
}

Note that we are not returning any response here since the REST method doesn't return anything. The status code of 204 indicates 'No Content'.

Deleting an issue

As you would expect, this doesn't need any additional data as the url has the issue key in it: http://localhost:8080/rest/api/2/issue/DEMO-13. The operation is DELETE as opposed to PUT.

private static void invokeDeleteMethod(String auth, String url) throws AuthenticationException, ClientHandlerException {
    Client client = Client.create();
    WebResource webResource = client.resource(url);
    ClientResponse response = webResource.header("Authorization", "Basic " + auth).type("application/json")
                .accept("application/json").delete(ClientResponse.class);
    int statusCode = response.getStatus();
    if (statusCode == 401) {
        throw new AuthenticationException("Invalid Username or Password");
    }
}


Well, that wraps up the basics. Everything else is just an extension of this. https://developer.atlassian.com/display/JIRADEV/JIRA+REST+API+Tutorials has a whole lot of examples with data for each operation. Also attached to this post is the Java project we used for this post.

Don't forget to have a look at the JIRA Development Cookbook.
rest-client.zip
File Size: 5 kb
File Type: zip
Download File

 


Comments

parthi
05/10/2012 2:42am

Nice 1 Jobin

keep them coming please

Reply
J-Tricks
05/10/2012 5:11am

Thanks Parthi. Glad you liked it :)

Reply
Fcornejo
06/26/2012 1:48am

Great job, it helped me a lot. Thanks!

Reply
Daniel
07/11/2012 6:22pm

How would you go about attaching a file to an issue using jersey?

Reply
frank
07/24/2012 3:46am

Very nice examples, thanks a lot!

Reply
mike
08/03/2012 9:58am

Thanks for the nice article. I am able to use rest api to get project information however stuck while trying to update an issue :(
my url:
http://jira.xxx.intra:8080/rest/api/latest/issue/ABC-111
my data:
{"fields":{"assignee":{"name":"MG Reader"}}}
but when I want to update the issue with this data, I get a response status of 405 Method Not Allowed.. what can be issue..

Reply
J-Tricks
08/06/2012 1:52pm

Do you have "Assign Issue" permission for the issue? Are you able to do this operation from UI when logged in as the user you are trying from REST?

Reply
mike
08/06/2012 3:10pm

yea.. everything is good but now I know the reason.. many of the put/post methods are not supported before jira 5.0.x and I have jira old version.. so now I am using Soap client..

thanks :)

Reply
Chandramohan M
10/03/2012 12:08pm

Hi,

I was able to create an issue through REST JSON but I would like to create an issue with attachment. Which api should I use? and how should I mention the attachment file in the code? I have mentioned my code below.

Code:

String auth = new String(Base64.encode("admin:admin"));

Client client = Client.create();
WebResource webResource = client.resource("http://localhost:8090/rest/api/2/issue/createmeta");
ClientResponse response1 = webResource.header("Authorization", "Basic " + auth).type("application/json").accept("application/json").get(ClientResponse.class);
JSONObject json = new JSONObject();
JSONObject objProject =new JSONObject();
JSONObject objTemp =new JSONObject();
objTemp.put("key", "TES");
objProject.put("project", objTemp.toString());
objProject.put("summary", "Testing Rest ");
objProject.put("description", "Descibing what REst can do ");
//json.put("fields", objProject.toString());
//json.put("summary", "Testing Rest ");
objTemp =new JSONObject();
objTemp.put("name", "Bug");
//JSONObject objIssuetype =new JSONObject();
objProject.put("issuetype", objTemp.toString());
//data = "{"fields":{"project":{"key":"DEMO" }, "summary":"REST Test ","issuetype": {"name":"Bug" }}}";
// {"fields":{"project":{"TP" :"TestProj"}, "summary":"Testing Rest ","issuetype": {"name":"Bugwa"}}}
json.put("fields", objProject.toString());
String data =json.toString();

private static String invokePostMethod(String auth, String url, String data) throws ClientHandlerException {
Client client = Client.create();
WebResource webResource = client.resource(url);

ClientResponse response = webResource.header("Authorization", "Basic " + auth).type("application/json")
.accept("application/json").post(ClientResponse.class, data);
int statusCode = response.getStatus();

Reply
daeyong.park
10/29/2012 11:45pm

Cool!!

Reply
sridhar
11/02/2012 10:14am

I working on Rest api to get the issue details based on jira cook book and i am getting this error when trying to access jira from a standalone java application.

Exception in thread "main" com.atlassian.jira.rest.client.RestClientException: org.codehaus.jettison.json.JSONException: JSONObject["summary"] is not a JSONObject.
at com.atlassian.jira.rest.client.internal.jersey.AbstractJerseyRestClient.invoke(AbstractJerseyRestClient.java:75)
at com.atlassian.jira.rest.client.internal.jersey.AbstractJerseyRestClient.getAndParse(AbstractJerseyRestClient.java:80)
at com.atlassian.jira.rest.client.internal.jersey.JerseyIssueRestClient.getIssue(JerseyIssueRestClient.java:109)
at dst.App.main(App.java:34)
Caused by: org.codehaus.jettison.json.JSONException: JSONObject["summary"] is not a JSONObject.
at org.codehaus.jettison.json.JSONObject.getJSONObject(JSONObject.java:458)
at com.atlassian.jira.rest.client.internal.json.JsonParseUtil.getNestedString(JsonParseUtil.java:116)
at com.atlassian.jira.rest.client.internal.json.IssueJsonParser.parse(IssueJsonParser.java:141)
at com.atlassian.jira.rest.client.internal.json.IssueJsonParser.parse(IssueJsonParser.java:54)
at com.atlassian.jira.rest.client.internal.jersey.AbstractJerseyRestClient$1.call(AbstractJerseyRestClient.java:85)
at com.atlassian.jira.rest.client.internal.jersey.AbstractJerseyRestClient.invoke(AbstractJerseyRestClient.java:54)
... 3 more

Reply
J-Tricks
11/02/2012 10:23am

How is your json object constructed? Maybe you can post the code here?

Reply
Sivanandam
02/01/2013 6:09am

Hi,
I am getting the following error while running the code.

In the Line : ClientResponse response = webResource.header("Authorization", "Basic " + auth).type("application/json")
.accept("application/json").get(ClientResponse.class);

Error:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The method accept(MediaType[]) in the type RequestBuilder is not applicable for the arguments (String)

Reply
J-Tricks
02/04/2013 4:11pm

What version of Jersey client is it?

Reply
Mizan
02/04/2013 1:21am

Hi JTricks ,
Thanks for providing this tutorial , I need to know whether I can create a customfield which will display value from the JSON array return by a Rest call ?
Thank you

Reply
J-Tricks
02/04/2013 4:12pm

Sorry, I didn't understand. You can get any custom fields value via REST. What is different here?

Reply
Mizan
02/04/2013 10:17pm

I have Vertigo SLA plugin installed , they provide some REST api ( https://confluence.valiantys.com/display/VSLA/REST+Access+Guide#RESTAccessGuide-Authorizations ) using this I can get Remaining value of the SLA customfield .
I need this value in a separate customfield so that It appears in the issue navigator/gadgets , At present this value is not shown in any report/gadget .
Can i create a new customfield type which can get this value OR any other way i can get it in the customfield ?
Thanks :)

J-Tricks
02/05/2013 7:13pm

You should probably create a Scripted field. Script runner plugin provides one.

Mizan
02/05/2013 9:15pm

Thank you J-Tricks , I will try this .

06/17/2013 1:17am

I admire your thoughts and your way of expressing and putting it in front of readers "Java client" is really something that I have seen after a long time. We need more writers like you. This is perfect blog for anyone who is looking for topics like this. It has got it all, information, benefits and overview.

Reply
Suresh M
06/24/2013 3:41am

It was too cool. and helped me really. thanks

Reply
mizan
07/08/2013 4:31am

Hi JTricks ,
Can I use JRJC /Rest in the post function I am developing ? This new post function creates issues on a remote JIRA instance .
Thanks :)

Reply
J-Tricks
07/08/2013 5:34am

Haven't tried it but I don't see an issue with that!

If that doesn't work (due to some conflicts), use the Jersey method explained above. That will surely work.

Reply
09/01/2013 11:41pm

A good blog always comes-up with new and exciting information about "Java - Exception handling" and while reading I have experience that this blog is really have all those quality that characterize a blog to be a good one.

Joey
07/08/2013 12:49pm

J-Tricks,

I keep getting this error for some reason...

com.sun.jersey.api.client.ClientHandlerException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:149)
at com.sun.jersey.api.client.Client.handle(Client.java:648)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:670)
at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:503)
at com.jtricks.JTricksRESTClient.invokeGetMethod(JTricksRESTClient.java:56)
at com.jtricks.JTricksRESTClient.main(JTricksRESTClient.java:24)
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Unknown Source)
at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
at sun.security.ssl.Handshaker.processLoop(Unknown Source)
at sun.security.ssl.Handshaker.process_record(Unknown Source)
at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at java.net.HttpURLConnection.getResponseCode(Unknown Source)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(Unknown Source)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler._invoke(URLConnectionClientHandler.java:240)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:147)
... 6 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
at sun.security.validator.Validator.validate(Unknown Source)
at sun.security.ssl.X509TrustManagerImpl.validate(Unknown Source)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(Unknown Source)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
... 21 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
at java.security.cert.CertPathBuilder.build(Unknown Source)
... 27 more

I'm using version 1.17.1... I didn't change any of the code much besides the BASE_URL (obviously).

Reply
J-Tricks
07/08/2013 1:38pm

This comes when you are connecting to an HTTPS connection. Make sure you import the public certificate of you JIRA instance to the keystore of the application which is using this code.

See https://confluence.atlassian.com/display/JIRA/Connecting+to+SSL+services. The principle is same.

Reply
08/08/2013 5:32am

Oh my God I was unaware of the facts you mentioned in your article "Java developer". It is so helpful that I am sure everyone will praise you for sharing this information. Wonderful work. This is something I was searching for many days. My thirst has been quenched now after reading your article. I am highly thankful to you for writing this article.

Reply
09/27/2013 3:25am

Nice helper

Reply
Lukas
10/02/2013 6:32am

Hello there,

i am also trying to create a rest client. Everything is working fine but i cant get the versions, always getting 404 error: My code is:

Client client2 = Client.create();
WebResource web2 = client2.resource("http://myinstance.com:8080/rest/api/latest/project/One/versions/");
ClientResponse response2 = web2.header("Authorization", "Basic " + auth).type("application/json").accept("application/json").get(ClientResponse.class);
String result2 = response2.getEntity(String.class);

I am just wondering because i can access all other directories. I am working with Jira 4.3.2 and using this documentation: https://docs.atlassian.com/jira/REST/latest/#d2e3195

Got any clue?

Best regards!

Reply
J-Tricks
10/02/2013 7:11am

Use the documentation for 4.3.2 :)

https://docs.atlassian.com/jira/REST/4.3.2/#id152601

Reply
Lukas
10/02/2013 9:24am

Jesus Christ... i am so bad ^^
Thanks a lot ;)

alekhya
02/12/2014 10:24pm

Hi,
I am getting the following error while running the code.

In the Line : ClientResponse response = webResource.header("Authorization", "Basic " + auth).type("application/json")
.accept("application/json").get(ClientResponse.class);

Error:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The method accept(MediaType[]) in the type RequestBuilder is not applicable for the arguments (String)

Reply
J-Tricks
02/13/2014 7:12am

Makes ure you are using the right version and also the correct classes (check the import statements) ;)

You can compare with the attached source code.

Reply
alekhya
03/04/2014 12:21am

i have added all th eimports still the same error

alekhya
03/04/2014 12:28am

the same error with the source code also could not parse the accept() method

cseppd
03/02/2014 10:34pm

My code:
Client client = Client.create();
WebResource webResource = lient.resource("http://localhost:8088/pSalesRESTfulWS/rest");
ClientResponse response=webResource.accept(MediaType.APPLICATION_JSON_TYPE).get(ClientResponse.class);

But get error:

Reply
J-Tricks
03/06/2014 8:40pm

Sorry, you are missing the error!

Reply
cseppd
03/11/2014 4:30am

My code: Client client = Client.create();
WebResource webResource = client.resource("http://222.222.222.40:8088/pSalesRESTfulWS/rest/MRTerritory/001472");
ClientResponse response = (ClientResponse)webResource.accept(new MediaType[]{MediaType.APPLICATION_JSON_TYPE}).get(ClientResponse.class);
error: in 2nd line-cannot access URI
webResource = client.resource("http://222.222.222.40:8088/pSalesRESTfulWS/rest/MRTerritory/001472");
class file for java.net.URI not found

Reply
J-Tricks
03/11/2014 5:39am

class file for java.net.URI not found.

Do you have all the dependencies in the project? Looks like URI class is not in classpath!

Reply
cseppd
03/13/2014 6:45am

In 3rd line:ClientResponse response = (ClientResponse)webResource.accept(new MediaType[]{MediaType.APPLICATION_JSON_TYPE}).get(ClientResponse.class); Get error: No suitable method found for accept(MediaType).Plz hlp..

s3
03/17/2014 2:35pm

Hi, I downloaded the code provided above and when I try and do an atlas-run I get the error message:

cannot find symbol
symbol : method accept(java.lang.String)
location: interface com.sun.jersey.api.client.RequestBuilder

I'm runing jdk 6 45, not sure why I'm getting the error.

Reply

Your comment will be posted after it is approved.


Leave a Reply


J tricks - Little JIRA Tricks