How To Build a JSON Pass-Through Proxy in OSB

This is a step-by-step guide on how to implement a pass-through JSON proxy in OSB. Download the full example.

WM1ufoF

JSON is cool, but OSB doesn’t recognize it as a first-class data format. OSB cannot validate it, cannot transform it, and cannot even add the smallest security token to the JSON payload.

Consumers though have begun to demand support for JSON, because tests show that the serialization overhead for JSON is several times  smaller than for XML.

What it takes to support JSON in OSB?

A JSON Proxy, Step by Step

Let’s walk through the process of creating a simple OSB JSON proxy.

Our proxy will pass the payload through OSB to a JSON business service without being inspected or modified.

While this type of implementation is very limited, it is a good first step in supporting JSON in your OSB deployments, and lets you answer “Yes, but” instead of a blunt “No” when being offered a budget.

Service Description

Pure REST Services

A typical REST service has a separate URL per object. Using different HTTP methods, the caller may create, update or delete that object.

For example, for the URL http://usermanagement/User/100500:

  • GET – retrieves the current user information with id 100500
  • PUT – creates the user with id 100500
  • POST – updates the user with id 100500
  • DELETE – removes the user with id 100500

Operation-Based REST

Some JSON services use a WSDL-influenced operation-based approach, where each URL is an operation, and the payload is passed to the operation with a POST method. This type of implementation is easier to implement with some of the existing tools, including OSB.

For example, the same HTTP POST method with the appropriate JSON payload is used for:

  • http://usermanagement/GetUser – retrieving a user
  • http://usermanagement/AddUser – creating a user
  • http://usermanagement/UpdateUser – updating a user
  • http://usermanagement/DeleteUser – removing a user

A Service I’m Going to Use

For the sake of this post, I will use a mixed approach service, which lets me demonstrate the handling of both the URI and the HTTP method parts.

Operation URL HTTP Method
Add user http://host:port/AddUser PUT
Update user http://host:port/UpdateUser POST

(For those interested in how I built a mock of the backend service, see the Addendum.)

Business Service

Let’s build the business service. I called mine JSONDirect.

2014-06-18_19-14-05

It must have a type of “Messaging Service”.

2014-06-18_19-15-55

It should have the request and response types set to “Text”.

2014-06-18_19-16-37

I target the business service to the mock proxy by providing the URL.

No more configuration is required, though for a real production service you may want to update the HTTP transport tab to set the authentication method and a service account.

Deliver OSB Parallel Services 5 Times Faster! Learn How

Entry Proxy

Entry proxy is responsible for capturing the call operation (relative URI), its method (HTTP method) and the payload, and to pass these unmodified to the business service.

The relative URI and the method should be updated in the $outbound variable to modify the behaviour of the business service accordingly.

Strictly speaking, you may also need to pass the query parameters value. Some REST services may use the query parameters; mine doesn’t and so I ignore it.

2014-06-18_19-14-05

The service type is set to the already familiar “Messaging Service”.

2014-06-18_19-15-55

And the request and response types are set to “Text”, too.

JSONP-URI

The entry proxy is listening on /JSONDirect URI.

JSONP-Proxy

The proxy implementation retrieves the consumer-provided operation (property relative-URI) and method (property http-method).

It then injects the values into the outbound request for the business service to consume.

(This implementation contains a bug.)

JSONP-OperationName

(1) Read the relative URI into variable $operationName.

The relative URI is the part of the URL after /JSONDirect/.

JSONP-HTTPM

(2) Read the HTTP method into variable $httpMethod

JSONP-Routing

(3) Route the request to the JSONDirect business service.

JSON-InsertURI

(4) Within the routing node, inject the relative URI ($operationName) into the outbound request.

The business service will append this value to the URL configured for it, reconstructing the same URI that was used by the consumer.

http://localhost:8001/JSONDirect + / + AddUser

JSONP-InsertMethod

(5) Within the same routing node, inject the HTTP method, making the business service use it instead of the default POST.

The entry proxy implementation is now complete.

Testing

Time to test our proxy. We’re going to validate 3 scenarios:

1. AddUser call with HTTP PUT (expected outcome: success with HTTP 200)
2. AddUser call with HTTP POST (expected outcome: a system error due to the invalid method, HTTP 500)
3. UpdateUser call with HTTP POST (expected outcome: a business error as designed, HTTP 500).

AddUser PUT

Request:

PUT http://localhost:8001/JSONDirect/AddUser HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/json
Content-Length: 71
Host: localhost:8001
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

{
	"FirstName":"John",
	"LastName":"Doe",
	"PhoneNumber":"4162221234"
}

Response:

HTTP/1.1 200 OK
Date: Fri, 20 Jun 2014 03:48:22 GMT
Content-Length: 42
Content-Type: text/plain; charset=utf-8
X-Powered-By: Servlet/2.5 JSP/2.1

{ 'Result':'Operation AddUser succeeded' }

All OK. (Not really! See below.)

AddUser POST

The request is the same, except for the POST method:

POST http://localhost:8001/JSONDirect/AddUser HTTP/1.1
...

Response:

HTTP/1.1 500 Internal Server Error
Date: Fri, 20 Jun 2014 03:49:38 GMT
Content-Length: 137
Content-Type: text/plain; charset=utf-8
X-Powered-By: Servlet/2.5 JSP/2.1

{ 'Result':'Operation AddUser failed: Not HTTP PUT Only HTTP PUT is allowed for AddUser method' }

The result is as expected: the backend service doesn’t accept POST, and the error is propagated to the caller.

UpdateUser POST

Request (note the /UpdateUser component of the URL):

POST http://localhost:8001/JSONDirect/UpdateUser HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/json
Content-Length: 71
Host: localhost:8001
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

{
	"FirstName":"John",
	"LastName":"Doe",
	"PhoneNumber":"4162221234"
}

Response:

HTTP/1.1 500 Internal Server Error
Date: Fri, 20 Jun 2014 03:52:16 GMT
Content-Length: 42
Content-Type: text/plain; charset=utf-8
X-Powered-By: Servlet/2.5 JSP/2.1

{ 'Result':'Operation UpdateUser failed' }

We’ve got HTTP 500 with the preconfigured error message.

I’ve Got a Bug!

Have you noticed the bug in my implementation?

The content type is returned as text/plain.

The service mock doesn’t have this issue. It sends back application/json, but the entry proxy loses it.

JSONP-ContentTypeBack

The solution is to either pass the Content-Type from the backend service to the consumer, or simply hardcode it to “application/json”.

The response with HTTP 500 (i.e. UpdateUser one) will go back as a fault, so you have to update the Content-Type header in both the response flow and the error handler.

That’s it!

Xgxj8Ta

Not rocket science, as you can see, though as usual for OSB, you have to learn a few gotchas.

Happy JSON’ing!

Addendum: Backend Service Mock

I’m not going to use an existing service, but build one specifically for this post. This lets me add special features to demonstrate and test the behaviour of the JSON proxy.

2014-06-18_19-19-59

I call my service JSONEcho, and it is implemented as an OSB proxy service.

First, our proxy must have the type “Messaging Service”. “Any SOAP Service” or “Any XML Service” won’t do anymore.

2014-06-18_19-20-28

Then, on the next tab, make sure both the request and response have a type of text.

2014-06-18_19-27-12

On the transport configuration tab, we enter the usual values, most notably the URI the service is listening on.

There is nothing special on any of the other tabs. Configure them as usual for any HTTP entry proxy.

JSONEchoMock

The implementation for JSONEcho is pretty simple:

  • Get the operation name.
  • For AddUser and HTTP PUT, return a success message in JSON format.
  • For UpdateUser, return a failure message in JSON format, and HTTP 500.
  • For all responses, set the Content-Type header to “application/json”.

JSONEcho-OperationName

(1) The mock extracts the operation name.

The operation name is passed as the part after the base URL.

JSONEcho-If

(2) Based on the operation name, one of the two code branches is executed.

JSONEcho-IfPut

(3) For AddUser, the service demands the use of the HTTP PUT operation.

(5) If the method is not PUT, raise an error.

JSONEcho-SuccessResult

(4) For the AddUser operation, the body content is replaced with a success message.

JSONEcho-FailedOperation

(6) For any other operation (including UpdateUser), the body content is replaced with a failure message.

JSONEcho-HTTP500

(7) Also, for the failed operation set the HTTP status code to 500.

2014-06-18_19-25-52

(8) Finally, add “Content-Type: application/json” header, as befits a well-written JSON service.

Testing the Mock Service

Let’s test it.

First let’s perform an AddUser JSON request with a PUT method, as required by our service:

PUT http://localhost:8001/JSONEcho/AddUser HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/json
Content-Length: 71
Host: localhost:8001
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

{
	"FirstName":"John",
	"LastName":"Doe",
	"PhoneNumber":"4162221234"
}

The response contains the success message, the HTTP status is 200, and the the content type is set to “application/json”. All as expected.

HTTP/1.1 200 OK
Date: Thu, 19 Jun 2014 01:50:15 GMT
Content-Length: 42
Content-Type: application/json; charset=utf-8
X-Powered-By: Servlet/2.5 JSP/2.1

{ 'Result':'Operation AddUser succeeded' }

The same AddUser request with a POST  method gives us an error (as designed):

HTTP/1.1 500 Internal Server Error
Date: Thu, 19 Jun 2014 20:51:40 GMT
Content-Length: 137
Content-Type: application/json; charset=utf-8
X-Powered-By: Servlet/2.5 JSP/2.1

{ 'Result':'Operation AddUser failed: Not HTTP PUT Only HTTP PUT is allowed for AddUser method' }

Now let’s test the UpdateUser operation. Note the URL has “UpdateUser” as the last component now.

POST http://localhost:8001/JSONEcho/UpdateUser HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/json
Content-Length: 71
Host: localhost:8001
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

{
	"FirstName":"John",
	"LastName":"Doe",
	"PhoneNumber":"4162221234"
}

The response has the failure message (as designed), HTTP status 500 and the correct content type.

TTP/1.1 500 Internal Server Error
Date: Thu, 19 Jun 2014 20:57:48 GMT
Content-Length: 42
Content-Type: application/json; charset=utf-8
X-Powered-By: Servlet/2.5 JSP/2.1

{ 'Result':'Operation UpdateUser failed' }

Good, our service is ready.

Vladimir Dyuzhev, author of GenericParallel

About Me

My name is Vladimir Dyuzhev, and I’m the author of GenericParallel, an OSB proxy service for making parallel calls effortlessly.

I’m building SOA enterprise systems for clients large and small for almost 20 years. Most of that time I’m working with BEA (later Oracle) Weblogic platform, including OSB and other SOA systems.

Feel free to contact me if you have a SOA project to design and implement. See my profile on LinkedIn.

I live in Toronto, Ontario, Canada.  canada   Email me at info@genericparallel.com


Tags: , ,
22 comments on “How To Build a JSON Pass-Through Proxy in OSB
  1. Farit says:

    How can I obtain POST content before sending it to the REST(business)service. I need it to compute signature.

  2. Farit says:

    Yes, that’s so. But when I’m looking log the payload contains some extra data

    I was trying to make text or binary business-service, but always got the same extra data. What is wrong?

  3. Farit says:

    Extra data was shielded
    <?xml version=”1.0″ encoding=”UTF-8″ ?>

  4. Sam says:

    I ran across this post while searching for a way to set the HTTP response code. It seems that you do set it to 500, but I can’t see how. My requirement is to only accept POST, so I would like to return a 501 if the GET method is invoked. Did I fail to find the solution above?

  5. Sorry for long delay, got very busy with work.

    In the code above the error handling branch sets the HTTP 500 code in the “Reply (with error)” block.

    If you need to reply with a specific HTTP code, you need to update the value of the http-response-code element under $inbound/ctx:transport/ctx:response in the response flow.

  6. henry says:

    Awesome blog!

    Was wondering if this process would be compatible with Oracle SOA Suite 11g Version 11.1.1.6? Thank you.

    • Yes, it works in 11g and in 12g (with some necessary but not major changes).

      In fact, my current PROD system is exactly .6, while the work is in progress to port to OSB 12 (some 2-3 years down the road).

      • henry says:

        Thanks for the quick response. I tried importing your example into my 11.1.1.6 server; however I got an error message stating that the jar file that is being imported is an unsupported version which is 11.1.1.7 while my version is 11.1.1.6.

        • Oh, thanks for reporting this! I’ll re-export all JARs with an older version.

          Meanwhile, if you feel adventurous :-), you may try and open the JAR as ZIP, and update the version in the ExportInfo file, line <imp:property name=”productversion” value=”11.1.1.7″/>.

  7. Deepak says:

    What changes I have to do to call a SOAP based business service.

    I mean My proxy will accept json and will make a call to SOAP based business service.

  8. Gabriel says:

    My team have been working with OSB 11.1.1.5 consuming Rest and HTTP services. Righ now we have to consume a service (old one) based on HTTP POST where the payload must be sent in a parameter(variable), i.e. xmlPayload=…
    I have achieved this using a concat of string + serialized body. Do you know any other way to solve it? My idea would be to manage it as I did with GET + http-parameters, but it doesn’t work with POST method

    Thanks!

    • Vlad says:

      Have you tried to set content-type to application/x-www-form-urlencoded? (I did not, but this is the content-type that encodes the parameters in the body – may be OSB recognizes it).

      Otherwise, I believe it is a right solution for an one-off service.

  9. senthil says:

    I am having some issue in relative-URI while invoking from JSON Direct to JSONECho. I logged the outbound of JSON Direct and inbound of the JSONECho.

    I can see the correct value for relative uri in outbound whereas inbound I can see the forward slash added in the relative-uri. so i am getting “The invocation resulted in an error.”

    /AddUser

    Please help me to fix this issue.

    • Vlad Dyuzhev says:

      It is a very puzzling behaviour. OSB logic that handles the relative-URI strips any leading slashes from the path, i.e., even if I use

      http://127.0.0.1:7001/JSONDirect//////AddUser

      I still get AddUser on both JSONDirect and JSONEcho.

      Could you please tell me the OSB version and how do you send the request (i.e. SOAPUI, OSB console, …)?

      I believe it is better to continue over email, due to size constraints.

      Please email me.

  10. Claudio I says:

    Hi Vladimir.

    When I invoke a REST/JSON API implemented with OSB 11g from WSO2 API Management console and API response contains
    “Content-Type: application/json; charset=utf-8”
    the WSO2 API Management display “Invalid”.
    I suppose the problem is the following

    “Content-Type: application/json; charset=utf-8” Invalid
    “Content-Type: application/json” Valid

    Can you suggest any workaround ?

    Thanks in advance.
    Caudio I.

    • Vlad Dyuzhev says:

      Hello, Claudio.

      I doubt that the charset part causes the issue. This is a standard format for Content-Type header, and if WSO2 had any problems with it, it would be unusable in any PROD environment.

      Now, I have tried to force OSB to send the Content-Type header with no charset part, and I couldn’t. The charset is appended to the header no matter what, even if I specified my own charset.

      The bottom line, the issue must be somewhere else. For instance, the response may be marked as UTF-8, but instead could be an ASCII text, causing UTF-8 parser on WSO2 side fail.

      If you need further help, try and catch the response as it is sent over the wire (e.g. with Fiddler). It may help to pinpoint the problem.

  11. Naveen says:

    Hi Vladimir,

    I am implementing REST Service with PUT method on OSB 11g. And i am passing the request in Query parameters . When i test the Business service i am getting proper response back, but when i test thru Proxy service i am getting below error. I do have disabled the option “Chunked stream” on BS.

    BEA-382502

    OSB Service Callout action received an error response

    <!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN””http://www.w3.org/TR/html4/strict.dtd”>
    <HTML><HEAD><TITLE>Length Required</TITLE>
    <META HTTP-EQUIV=”Content-Type” Content=”text/html; charset=us-ascii”></HEAD>
    <BODY><h2>Length Required</h2>
    <hr><p>HTTP Error 411. The request must be chunked or have a content length.</p>
    </BODY></HTML>

    411

    Could you please advice what would be the issue.

    Thanks in Advance.

    regards,

    • Vlad Dyuzhev says:

      PUT method must have a body. From your description, it looks like your backend doesn’t receive any body and hence complains.

      I’d suggest to point your Biz to a proxy such a Fiddler and see what exactly ESB sends in “good” and “bad” scenarios.

  12. Rakesh says:

    Hi Valdimir,

    I have an issue with reading the REST Outbound context response in OSB 12C.

    Able to invoke the REST service but not able to access the Response metadata.

    OSB-382046

    Failed to marshall the value of context variable “outbound” to XML: error: Unexpected element: CDATA

    Any suggestions please.

    Thanks
    Rakesh

    • Vlad Dyuzhev says:

      Do you have you proxy as Messaging Service/Text? If I remember my experiments correctly, if you try to pass a non-XML response via a XML proxy in OSB12, it still tries to parse it, and fails.

      In GPS I have two proxies in a line – a WSDL-based calls a Text one. That seems to do the trick, tho I do not remember all the combinations that I have tried.

Leave a Reply

Your email address will not be published. Required fields are marked *

*