0

I've been developing an application that provides a REST service. I have some tested code that I run against it to see if it works okay.

When running it against the application deployed on my local Weblogic development server, it works fine.

However, when I deployed it on another Weblogic server on a Red Hat machine, I get 400 Bad Request errors.

Here is the client code I'm using to test the service:

    Client client = Client.create();
    //WebResource webResource = client.resource("http://10.1.1.2:7001/NotificationFramework/rest/notifications/createNotification");
    WebResource webResource = client.resource("http://rhvm:7003/NotificationFramework/rest/notifications/createNotification");

    ClientResponse clientResponse = webResource.type("application/json").post(ClientResponse.class, testJsonObject.toString());
    JSONObject response2 = new JSONObject(clientResponse.getEntity(String.class)); 
    System.out.println(response2);

The commented line is the one on my local machine.

Here is the response I'm getting:

An error occurred: Server returned HTTP response code: 400 for URL: http://rhvm:7003/NotificationFramework/rest/notifications/createNotification

And here is an excerpt of the code providing the REST service:

@Path("/notifications")
public class RestServices {     

    @POST
    @Path("/createNotification")
    @Consumes( {MediaType.APPLICATION_JSON} )
    @Produces( {MediaType.APPLICATION_JSON} )
    public static NotificationResponse createNotification(JAXBElement<Notification> n) {
// do some stuff

return notificationResponse;
}

I've already tried putting an extra / on the end. And I've tested it with the RESTClient add-on for Firefox and I get the exact same behaviour.

Any help would be greatly appreciated.

Thanks in advance.

// Edit

I discovered that it's something to do with the JAXBElement.

The following services works:

@POST
@Path("testRest3")
@Consumes( {MediaType.APPLICATION_JSON} )
@Produces({MediaType.APPLICATION_JSON})
public static NotificationResponse testRest3() {
    logger.info("yo3");

    return new NotificationResponse(101, "yo");
}

but the following doesn't:

@POST
@Path("testRest4")
@Consumes( {MediaType.APPLICATION_JSON} )
@Produces({MediaType.APPLICATION_JSON})
public static NotificationResponse testRest4(JAXBElement<Notification> n) {
    logger.info("yo4");

    return new NotificationResponse(101, "yo");
}

I checked the Notification class as recommended by pestrella and found that @XmlRootElement was missing. I added this but this still hasn't fixed the problem. I'm not sure if it should be @Xml.. but I'm new to this. Following the tutorial from vogella.

Here is my Notification class:

@XmlRootElement
public class Notification {
    private int applicationId;
    private int notificationId;
    private int priority;
    private String message;
    private String detail;
    private String appUrl;

// methods and stuff

}

And here is the body as submitted with the RESTClient add-on for Firefox:

{"appUrl":"","message":"my message","notificationId":1110001,"detail":"my detail","priority":3,"applicationId":111}
the_new_mr
  • 3,476
  • 5
  • 42
  • 57
  • Your code looks fine. If you are absolutely sure that you have deployed the same code, and that your test code is the same, I would look in your Weblogic logs first to determine what is causing the `400` response. Look for exceptions. Depending on how you've put your Jersey REST components together you might find that your `MessageBodyReader` cannot deserialize the incoming `POST` body because it is malformed. Can you add your `Notification` class code to the question as well as the `POST` body you send via RESTClient? – pestrella Mar 15 '13 at 15:58
  • Your comment has gotten me much closer. I made some dummy services and zoned in on the problem. It is, as you suggested, something to do with the way it's trying to get the data from the Notification object (n). I looked at my Notification class and found that '@XmlRootElement was missing. I added it but it still hasn't solved the problem (though the fact that it's called '@XmlRootElement makes me think that that's not right either... but I couldn't find '@JsonRootElement... I'm new to all this). I've added the stuff you asked to see. Thanks for helping with this. I really appreciate it. – the_new_mr Mar 15 '13 at 17:35
  • Oh, I checked the weblogic logs but not exceptions as far as I could see anyway. – the_new_mr Mar 15 '13 at 17:42
  • So, I found that I can just pass the JSON Object as a 'String' argument to the method. I'm considering just doing that and using Jackson to create a JSON Object out of it and then converting it to a Notification object. It's manual work but at least it would work. Ideally, I'd like to get the JAXBElement stuff working. – the_new_mr Mar 15 '13 at 17:54
  • Glad you made some progress. It's fine to use a `String` object and perform the marshalling yourself with Jackson. I also prefer to marshal objects myself using Jackson, but it should be easy enough to get JAXB working :) – pestrella Mar 15 '13 at 18:12

1 Answers1

1

A 400 response in this instance can indicate some sort of error while unmarhsalling the POST body.

Using the @XMLRootElement is fine. However, you might have problems getting JAXB to unmarshal to primitive types (depends on what version you've got).

The safest way to get JAXB to unmarshal your Notification object is to use the Integer type instead of a primitive int type.

@XmlRootElement
public class Notification {
    private Integer applicationId;

    /* and the rest... */
}

Also, you should not need to wrap the Notification object with JAXBElement if you are using the @XmlRootElement annotation. Try removing the JAXBElement wrapper:

@POST
@Path("testRest")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public static NotificationResponse testRest(Notification n) {
    logger.info("yo!");
    return new NotificationResponse(101, "yo");
}

If the problem persists, then you can always use a MessageBodyReader to unmarshal request bodies manually.

The typical way to do this for JSON request bodies is to implement MessageBodyReader and use a JSON parser of your choice, like Gson or Jackson.

@Provider
@Consumes("application/json")
public class CustomJsonReader<T> implements MessageBodyReader<T> {

    @Override
    public boolean isReadable(Class<?> type, Type genericType,
            Annotation[] annotations,MediaType mediaType) {
        return true;
    }

    @Override
    public T readFrom(Class<T> type, Type genericType, Annotation[] annotations,
            MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
            InputStream entityStream) throws IOException, WebApplicationException {

        /* Convert the request body (passed in as InputStream) to a String.
         * Here I'm using apache commons IOUtils to convert the input stream
         */
        StringWriter writer = new StringWriter();
        IOUtils.copy(entityStream, writer, "UTF-8");
        String json = writer.toString();

        /* Use JSON parser to unmarshal the JSON to a complex object */
        return new Gson().fromJson(json, genericType);
    }
}
pestrella
  • 9,786
  • 4
  • 39
  • 44
  • Tried changing int to Integer but no joy. Just curious by the way, why do you prefer to marshal stuff yourself instead of using something like JAXB? – the_new_mr Mar 15 '13 at 18:20
  • Try not wrapping the `Notification` object with `JAXBElement` as it's redundant when using the `@XmlRootElement` annotation. I prefer marshalling objects myself using Jackson (or GSON) inside a `MessageBodyReader` in scenarios where a want greater flexibility. It's a little more involved, but if you decide to go in that direction then here's how I've done it in the past: http://stackoverflow.com/a/14751029/551467 – pestrella Mar 15 '13 at 18:34
  • I tried removing the wrapper but still not working. Can you think of any reason why? Think I'm just gonna marshal it myself as I'm going a bit nuts. Thanks for the link. If you edit your answer to say just to marshal it myself, I'll accept it since your comments and answer helped me at least to get a solution (even if it's manual). I would really like to do it automatically though. – the_new_mr Mar 19 '13 at 15:47
  • Also, I wish I was working somewhere where there was someone like you to learn from! – the_new_mr Mar 19 '13 at 15:48
  • Don't mention it, glad to assist. Yes, try the `MessageBodyReader` (I've updated my answer). Good luck. – pestrella Mar 19 '13 at 16:44