ibmmaximorestjsonapis / maximorestclient

The Maximo REST client library provides a set of driver API's which can be consumed by an JAVA based web component that would like to interface with a Maximo instance.
Apache License 2.0
42 stars 43 forks source link

MaximoConnector.create throws unexpected error on Bad Request response #13

Open bloodgain opened 7 years ago

bloodgain commented 7 years ago

In MaximoConnector.create, on a response code >= 400, the library attempts to decode the HTTP error stream as a JsonObject. When the response code is 400 (Bad Request), the error stream contains only the response headers, which are not valid JSON, resulting in javax.json.stream.JsonParsingException being thrown. Because the error thrown is an unexpected subclass of IOException instead of an OslcException, the error is not informative ("Unexpected char") and difficult to handle gracefully.

This condition may exist in other methods that do similar processing, including MaximoConnector.getAttachmentData.

Also note that con.getErrorStream() can return null in some conditions, which would result in Json.createReader throwing NullPointerException.

ibmmaximorestjsonapis commented 7 years ago

Hi,

I was trying to invetigate this issue but have no luck to reproduce it. There should always be a json response when error code >= 400. Could you please provide reproduce steps if that's possible?

Thanks, Yajin Zheng

bloodgain commented 7 years ago

It's been a little while since I found this, but I believe all I did was leave out a required field. I think I was actually testing whether our siteid was a required field. It should have been determinable the same way as location, which is auto-populated via assetnum from the asset object. However, I believe I got a similar error when any required field was left out of the work order object I was trying to create.

At the moment, it appears our test system will allow me to make any work order, even if it's missing a critical field like assetnum, so even I can't reproduce the error. It should be returning an error, though. I'll report it to our administrator. If you're not able to reproduce it by trying to create an incomplete work order, I'll try to reproduce it later when we reconfigure our test system correctly.

ibmmaximorestjsonapis commented 7 years ago

So I try to remove the required field like siteid for asset and get this exception:

Exception in thread "main" com.ibm.maximo.oslc.OslcException: BMXAA4153E - null is not a valid site. Enter a valid Site value as defined in the Organization Application. at com.ibm.maximo.oslc.MaximoConnector.create(MaximoConnector.java:505) at com.ibm.maximo.oslc.MaximoConnector.create(MaximoConnector.java:462) at com.ibm.maximo.oslc.ResourceSet.create(ResourceSet.java:616) at com.ibm.maximo.oslc.TestOSLCApi.main(TestOSLCApi.java:196)

So please let me know the steps if you have any chance to reproduce it.

bloodgain commented 7 years ago

After some back-and-forth with our Maximo admin, I've managed to reproduce it. I'm trying to create a work order, and the (lean mode) JSON† that gets sent to Maximo here https://github.com/ibmmaximorestjsonapis/maximorestclient/blob/f450f4a9b5c8ebe44eb5abcd0e987eb9745f4097/src/com/ibm/maximo/oslc/MaximoConnector.java#L509 is:

{
  "assetnum": "12345",
  "description": "TEST - JSON API WO creation",
  "status": "WAPPR",
  "worktype": "CM",
  "siteid": null,
  "orgid": "myorg",
  "faildate": "2017-08-30T14:33:33.124-05:00",
  "failurecode": "PARTFAILURE",
  "fr1code": "AC01",
  "fr2code": "R02",
  "problemcode": "P00",
  "jpnum": null
}

The returned response code is 400, and the buffer in the inputStream variable decodes to:

'HTTP/1.1 400 Bad Request\r\nContent-Type: text/html\r\nContent-Language: en-US\r\nServer: Microsoft-IIS/7.5\r\nX-Powered-By: Servlet/3.0\r\nX-Powered-By: ASP.NET\r\nx-frame-options: SAMEORIGIN\r\nDate: Wed, 30 Aug 2017 19:38:49 GMT\r\nConnection: close\r\n\r\nBad Request\x00\x00\x00'
[many more null chars truncated from the end]

with a URL of: https://subdomain.internalURL.com:443/maximo/oslc/os/mxwo?&collectioncount=1

When I use the same JSON to make a request in SoapUI, it returns:

HTTP/1.1 400 Bad Request
Cache-Control: no-cache="set-cookie, set-cookie2"
Content-Type: text/html
Content-Language: en-US
Expires: Thu, 01 Dec 1994 16:00:00 GMT
Server: Microsoft-IIS/7.5
X-Powered-By: Servlet/3.0
Set-Cookie: MAXIMO_MYORG_JSESSIONID=0000xfoobarSESSIONID123_:-1; Path=/; Secure; HttpOnly
X-Powered-By: ASP.NET
x-frame-options: SAMEORIGIN
Date: Wed, 30 Aug 2017 19:41:30 GMT
Connection: close

Bad Request{"Error":{"message":"BMXAA4153E - null is not a valid site. Enter a valid Site value as defined in the Organization Application.","statusCode":"400","reasonCode":"BMXAA4153E","extendedError":{"moreInfo":{"href":"https:\/\/subdomain.internalURL.com\/maximo\/oslc\/error\/messages\/BMXAA4153E"}}}}

I see the JSON in that response, though even SoapUI says it can't be viewed as JSON. If I remove the siteid and jpnum values so there are no explicit nulls, SoapUI gets the same response.

So I see two issues at the moment:

  1. The client library is getting a slightly different response than if I use SoapUI to POST JSON to the same URL. Not sure why.
  2. In either case, the response does not appear to be valid JSON that can be decoded to a JsonObject, which results in the stack trace (line numbers may be offset in current version):
javax.json.stream.JsonParsingException: Unexpected char 66 at (line no=1, column no=1, offset=0)
    at org.glassfish.json.JsonTokenizer.unexpectedChar(JsonTokenizer.java:532)
    at org.glassfish.json.JsonTokenizer.nextToken(JsonTokenizer.java:415)
    at org.glassfish.json.JsonParserImpl$NoneContext.getNextEvent(JsonParserImpl.java:222)
    at org.glassfish.json.JsonParserImpl$StateIterator.next(JsonParserImpl.java:172)
    at org.glassfish.json.JsonParserImpl.next(JsonParserImpl.java:149)
    at org.glassfish.json.JsonReaderImpl.readObject(JsonReaderImpl.java:101)
    at com.ibm.maximo.oslc.MaximoConnector.create(MaximoConnector.java:503)
    at com.ibm.maximo.oslc.MaximoConnector.create(MaximoConnector.java:461)
    at com.ibm.maximo.oslc.ResourceSet.create(ResourceSet.java:616)
    ...(calls from my code)

Even if I managed to create a JsonObject with invalid fields, Maximo's response should be parseable. Maybe that means this is an issue against Maximo, but the client library should probably be robust against bad responses from the server. I have to enter debug mode with a copy of the library to find out what the response was that caused the exception(s).

† specific information has been made generic in JSON and HTML responses here, but is valid for our test system