JAX-RS has a bunch of built-in handlers that can marshal to and from a few different specific Java types. While most are low-level conversions, they can still be useful to your JAX-RS classes.
If we use UriInfo then JAX-RS Runtime will understand that the data is coming from the req URI so that it will inject the data as input to our Resource method.
Similarly, If we wanted to read the data from the body we need to take InputStream, Reader, File, byte[], String, char[], MultivaluedMap<String, String> and Form Input, javax.xml.transform.Source any one of these as method param which has been provided by JAX-RS API so that JAX-RS will understand the client is sending the data as part of the req body so it will populate the data as raw data as input to our method and if we want we can take anything as return type as well.
But we can use StreamingOutput to send the response back to the client but not for to take the input.
Purpose of Built-in Marshallers:
1) javax.ws.rs.core.StreamingOutput
Used to write raw data to the client directly to the client as part of the response body.
2) java.io.InputStream
It used to take in the form of byte format, we take it as input param and return type as well.
3) java.io.Reader
Character format and we can take it as input param. It allows us to read the data in any language but InputStream will not allow that means if we wanted to read internationalization or different locale languages format data then we need to use Reader which will read the data in a character format in an internationalized manner.
That means the client as part of the browser can send any encoded format in any locale and client will puts that locale as part of the Header and he will sends to the Resource now the Resource will takes the Header locale name and reads the data as per the encoded format so that Resource can decodes without any precision loss in order to do this we need to use Reader bcz it can supports for Internationalization.
4) byte[]
Used to read bytes of data and we can take input and return type as well If we take InputStream then we need to iterate over the InputStream to collect/read the data instead of this if we take byte[] then JAX-RS will collects the data from the InputStream and gives as byte[] directly to the developers.
5) String-We can take input and return type as well
6) char[]-We can take input and return type as well
7) java.io.File- We can take input and return type as well
8) MultivaluedMap<String, String> and Form Input
MultivaluedMap is used to read the data from the form fields that has been submitted from the JSP Form with knowing the details of the form fields details and we wanted to collect all the fields of the from dynamically then we need to use this. The method must be POST only.
It can be taken as return type as well if we wanted to send Headers as response so that we can put key as header name and its values as value in the MultivaluedMap.
9) javax.xml.transform.Source
How we can model the Resource which will takes the data as input from the req body?
Ans: We can use pre-defined Content Handlers that has been provided by the JAX-RS API which will permits us to read the data from the req body and permits us to send the response as part of the response body.
The client wants to upload file as input to the Resource class and Resource wants to send the file as response then how can we do this?
Ans: We can use File as input param to our Resource method so that client can upload a file and if we take return type as File the Resource can send file as response.
1) javax.ws.rs.core.StreamingOutput:
StreamingOutput is a simple callback interface that we need to implement when we want to do raw streaming of response bodies.
The StreamingOutput obj is used to write a raw data to the client directly as part of the response body, But if we write this class then we need to send the data to the client instead of this JAX-RS has provided callback mechanism which will provides the StreamingOutput obj and once we committed then it will takes and sends back to the client.
public interface StreamingOutput {
void write(OutputStream output) throws IOException, WebApplicationException;
}
We allocate implemented instances of this interface and return them from our JAXRS resource methods. When the JAX-RS runtime is ready to write the response body of the message, the write() method is invoked on the StreamingOutput instance. Let’s look at an example.
@Path("/myservice")
class MyService {
@GET
@Produces("text/plain")
public StreamingOutput get() {
return new StreamingOutput() {
@Override
public void write(OutputStream output) {
output.write("hello world".getBytes());
}
};
}
}
Here, we’re getting access to the raw java.io.OutputStream through the write() method and outputting a simple string to the stream. Here we are recomended to use an anonymous inner class implementation of the StreamingOutput interface or final final inner class rather than creating a separate public class. Since the StreamingOutput interface is so tiny, So beneficial to keep the output logic embedded within the original JAX-RS resource method so that the code is easier to follow. Usually, we are not going to reuse this logic in other methods, so it doesn’t make much sense to create a specific class.
Advantage of making OutputStream as a Callback mechanism:
We may have a doubt by ourself, "Why not just inject an OutputStream directly? Why have a callback object to do streaming output? The reason for having a callback object is that it gives the JAX-RS implementation freedom to handle output however it wants. For performance reasons, it may sometimes be beneficial for the JAXRS implementation to use a different thread other than the calling thread to output responses.
More importantly, many JAX-RS implementations have an interceptor model that abstracts things out like automatic GZIP encoding or response caching. Streaming directly can usually bypass these architectural constructs. Finally, the Servlet 3.0 specification has introduced the idea of asynchronous responses. The callback model fits in very nicely with the idea of asynchronous HTTP within the Servlet 3.0 specification.
2) java.io.InputStream:
If we take java.io.InputStream as method param then JAX-RS will introspects whether these are pre-defined Content types or not and understands that the client is sending the data as part of the body so that he will collects the data populates into the method param as input and if anything other than predefined like Order obj as input which is representing the req body data then it will not understands.
If we want, we can take Query, Path and Matrix params or any JAX-RS Injection mechanisms along with the req body as a method params.
We should not take multiple InputStreams as params to the method bcz one InputStream represents one body of the req that means we cannot have multiple bodies to a req hence we should not take multiple InputStreams as input.
@Path("/order")
class OrderResource {
@POST
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
@Path("/new/in")
public StreamingOutput newOrder(InputStream is) throws IOException {
// read the data from the InputStream
buffer = new StringBuffer();
while ((c = is.read()) != -1) {
buffer.append((char) c);
}
final String data=buffer.toString();
// Anonymous inner class
return new StreamingOutput() {
@Override
public void write(OutputStream os) {
os.write(data.getBytes());
os.close();
}// write
};// returns StreamingOutput obj which is return type of the newOrder method
}// newOrder
}
Alternate for Anonymous inner class Using final inner class
@Path("/order")
class OrderResource {
@POST
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
@Path("/new/in")
public StreamingOutput newOrder(InputStream is) {
int c = -1;
buffer = new StringBuffer();
while ((c = is.read()) != -1) {
buffer.append((char) c);
}
dataWriter = new DataWriter(buffer.toString());
return dataWriter;
}// newOrder()
private final class DataWriter implements StreamingOutput {
public DataWriter(String data) {
this.data = data;
}
@Override
public void write(OutputStream os) {
os.write(data.getBytes());
os.close();
}
}
}
Access the application:
http://localhost:8083/1.1CH-InputStream/resources/order/new/in
Select method as POST
Click on FORM option to tell content-type
Content-Type=text/plain
Click below Raw option on Payload/Body and send the data as part of the body as follows
orderId=101,product=Apples, quantity=12
Response:
orderId=101,product=Apples, quantity=12
3) java.io.Reader:
For reading request message bodies, we can use a raw InputStream or Reader for inputting any media type. For example:
@Path("/order")
class OrderResource {
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
@Path("/new/reader")
public StreamingOutput newOrder(Reader reader) {
buffer = new StringBuffer();
buf = new char[256];
while ((reader.read(buf)) != -1) {
buffer.append(buf);
}
dataWriter = new DataWriter(buffer.toString());
return dataWriter;
}
}
private final class DataWriter implements StreamingOutput {
...
}
}
Access the application:
http://localhost:8083/2ContentHandlers/resources/order/new/reader
Select method as POST
Click on FORM option to tell content-type
Content-Type=text/plain
Click below Raw option on Payload/Body and send the data as part of the body as follows
orderId=101,product=Apples, quantity=12
Response:
orderId=101,product=Apples, quantity=12
4. byte[]
If we take InputStream then we need to iterate over the InputStream to collect/read the data instead of this if we take byte[] then JAX-RS will collects the data from the InputStream and gives as byte[] directly to the developers.
A raw array of bytes can be used for the input and output of any media type.
If cleint sends the xml then pass this byte[] to the DOM parser to parse the xml.
@Path("/order")
class OrderResource {
@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.TEXT_PLAIN)
@Path("/new/bytes")
public StreamingOutput newOrder(byte[] raw) {
StringBuffer buffer = new StringBuffer();
for (byte c : raw) {
buffer.append((char) c);
}
DataWriter writer = new DataWriter(buffer.toString());
return writer;
}
private final class DataWriter implements StreamingOutput {
...
}
}
Access the application:
http://localhost:8083/2ContentHandlers/resources/order/new/bytes
Select method as POST
Click on FORM option to tell content-type
Content-Type=text/plain
Click below Raw option on Payload/Body and send the data as part of the body as follows
orderId=101,product=Apples, quantity=12
Response:
orderId=101,product=Apples, quantity=12
5) String:
Most of the data formats on the Internet are text based. JAX-RS can convert any text-based Format to and from either a String or an array of characters.
@Path("/order")
class OrderResource {
@POST
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
@Path("/new/string")
public StreamingOutput newOrder(String order) {
return new DataWriter(order);
}
}
For JAX-RS resource methods that return a String or an array of characters, we must specify the @Produces annotation so that JAX-RS knows what media to use to set the Content-Type header.
The JAX-RS specification does require that implementations be sensitive to the character set specified by the Content-Type when creating an injected String. For example, here’s a client HTTP POST request that is sending some text data to our service.
Method type POST, Content-Type: application/xml;charset=UTF-8
<customer>...</customer>
The Content-Type of the request is application/xml, but it is also stating the character encoding is UTF-8. JAX-RS implementations will make sure that the created Java String is encoded as UTF-8 as well.
Access the application:
http://localhost:8083/2ContentHandlers//resources/order/new/string
Content-Type=text/plain
Response:
orderId=101&product=Apples&quantity=12&price=10
6) char[]:
@Path("/order")
class OrderResource {
// It is not working check once in the google
@POST
@Consumes(MediaType.TEXT_XML)
@Produces(MediaType.TEXT_PLAIN)
@Path("/new/chars")
public StreamingOutput newOrder(char[] order) {
return new DataWriter(new String(order));
}
}
Access the application:
http://localhost:8083/2ContentHandlers//resources/order/new/chars
Content-Type=text/plain
Response:
It is not working properly so check once in the Google.
7) MultivaluedMap<String, String> and Form Input:
HTML forms are a common way to post data to web servers. Form data is encoded as the application/x-www-form-urlencoded media type. By using @FormParam annotation to inject individual form parameters from the request. Similarly we can also inject a MultivaluedMap<String, String> that represents all the form data sent with the request.
The data has been sent as part of the FORM from the req Body then we need to use MultivaluedMap<String, String> to collects the data in the form key and value directly.
@Path("/order")
class OrderResource {
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_PLAIN)
@Path("/new/form")
public StreamingOutput newOrder(MultivaluedMap<String, String> requestParams) {
StringBuffer buffer = new StringBuffer();
for (String paramName : requestParams.keySet()) {
List<String> paramValues = requestParams.get(paramName);
....
}
writer = new DataWriter(buffer.toString());
return writer;
}
private final class DataWriter implements StreamingOutput {
...
}
}
Here, our newOrder() method accepts POST requests and receives a Multivalued Map<String, String> containing all our form data. We may also return a MultivaluedMap of form data as your response.
The JAX-RS specification does not say whether the injected MultivaluedMap should contain encoded strings or not. Most JAX-RS implementations will automatically decode the map’s string keys and values. If we want it encoded, you can use the @javax.ws.rs.Encoded annotation to notify the JAX-RS implementation that we want the data in its raw form.
Access the application:
http://localhost:8083/2ContentHandlers/resources/order/new/form
Select method as POST
Click on FORM option to tell content-type
Content-Type=application/x-www-form-urlencoded
Click below FORM option on Payload/Body and send the data as part of the body as follows
orderId=101
product=Apples
quantity=12
price=10
Response:
product : Apples quantity : 12 price : 10 orderId : 101
8) java.io.File:
Instances of java.io.File can also be used for input and output of any media type. Here’s an example for returning a reference to a file on disk.
Now user/client can attach file over the Http Request and this will be converted as binary bits/bytes format and places as part of the req, so we need to take ContentType as @Consumes(MediaType.MULTIPART_FORM_DATA) that means it contains some char data and binary data as well the we need to use File as a method param.
@Path("/order")
class OrderResource {
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
@Path("/bulk/file")
public StreamingOutput newBulkOrder(File file) {
int c = 0;
data = new StringBuffer();
is = new FileInputStream(file);
while ((c = is.read()) != -1) {
data.append((char) c);
}
dataWriter = new DataWriter(data.toString());
return dataWriter;
}
}
Access the application:
http://localhost:8083/2ContentHandlers//resources/order/bulk/file
Select as POST
ContentType=multipart/form-data
Select the File option in the ARC and choose file and upload a file and send the req and we can add multiple files also at a time JAX-RS will reads but it treats as one single file only.
Response:
We will get whatever file uploaded as response.
Use Case:
Whenever an Android application wants to upload a file then we need to use File as param at the Resource side so that it can reads the file attached by the Android client. Here file may be a Excel Sheet/CSV file.
Note:
If we wanted to take multiple files then take File[] as param.
Internal working flow of File in case of JAX-RS:
Generally File will never points to the remote network request locations to collects the data that is the reason whenever the user uploads a file the client programme or browser or ARC will reads and converts into the binay and places in the req body then forwards to the Resource at the server side now JAX-RS runtime will takes the req and matches with the Resource method and if the methods is taking File as param or MultiValued param as input param then it will reads whole binary data and creates one temp file with in the local system at the server side and then File will points to the temp file then it will starts reading and passes data to the method param.
General problems while working with Files in RESTful:
Sometimes File systems will not works in the production system bcz if system that is there in the server side will not have read and write access to the disk the reason, hence we need to give permissions to the disk to work with files in the production or our development systems in case of Linux systems.
Checking practically whether the temp file created or not:
In order to check this temp file created or not 1st put debug point on the file while reading then Start the server in the debug mode then check the path of the file which temp file name then press F6 to know the flow of execution.
To see the file another click on windows button then type "%temp%" we can see the file while it is server is in Debug mode only.
9) javax.xml.transform.Source:
XSLT (Extensible Stylesheet Language Transformations) is a language for transforming XML documents into other XML documents, or other formats such as HTML for web pages, plain text or into XSL Formatting Objects, which may subsequently be converted to other formats, such as PDF, PostScript and PNG.
The javax.xml.transform.Source interface represents XML input or output. It is usually used to perform XSLT transformations on input documents.
JAX-RS inject a javax.xml.transform.Source instance that represents our request body and we’re transforming it using an XSLT transformation.
Except for JAXB, javax.xml.transform.Source is the only XML-based construct that the specification requires implementers to support. It it is a little strange that we can’t automatically inject and marshal org.w3c.dom.Document objects. This was probably just forgotten in the writing of the specification.
That means instead of using JAXb we can use javax.xml.transform.Source to convert into obj format but it is very difficult that's the reason we are using JAX-B to convert easily in to obj format.
Working with different types of return types:
1. StreamingOutput
2. byte[]
Instead of using StreamingOutput to write the data to the OutputStream we can take return type as
3. byte[] then JAX-RS will takes byte[] and reads and writes to the OutputStream.
If we wanted to return data as binary format then we can use byte[] as return type.
Access the application
http://localhost:8083/2ContentHandlers/resources/order/new/out/byte
Content-Type=text/plain
Send the req with data as follows
orderId=101,product=Apples, quantity=12
Response:
orderId=101,product=Apples, quantity=12
4. String
If we wanted to return data in the form of character format then we can use String directly.
Access the application
http://localhost:8083/2ContentHandlers/resources/order/new/out/string
Response:
orderId=101,product=Apples, quantity=12
5. char[]
Ckeck once it is not working just ckeck in the google
6. File[]
If we wanted to return a file to the client after performing the req processing to download file by the client we can take File as return type.
Access the application:
http://localhost:8083/2ContentHandlers/resources/order/new/out/file
Select as POST
ContentType=text/plain
Send the req with data as follows
orderId=101,product=Apples, quantity=12
Response:
Response will be written to file D:\product.txt
Note:
Similarly we can some more built types as as return types
How do you debug the Web services if any problems has been occurred in the application and where do you need to start to debug?
Ans: As Web services is an Distributed tech so we cannot easily debug it bcz it will be run on remote server so the only way to debug is by looking at the Logging file.
Is there any other purpose for logging apart from the debugging?
Yes, We can understand the health of the system and how many no.of req's are coming to our application and how many no.of users are repeatedly accessing our application all such kind info will be available can be get by using the Logging.
2 Comments
Great share!
ReplyDeletenice post . Thank you for posting something like this
ReplyDelete