restjava-8jax-rsweblogic12cstateless-session-bean

Is it possible to extract the original message body string (i.e., XML string or JSON string) within the "post()" method of a REST service?


QUESTION:
Is it possible to extract the original message body string (i.e., XML string or JSON string) - within the "post()" method - of a REST service?

Environment

Java 8

WebLogic 12.1.3 (w/ jax-rs(2.0,2.5.1) deployable library)

(The "request.getInputStream()" yields nothing... Seems that "read()" has already been applied "internally". Also, "mark()" or "reset()" is not supported)

"post()" method...

    package aaa.bbb.ccc;

    import javax.ejb.Stateless;
    import javax.ws.rs.*;
    import javax.ws.rs.core.MediaType;
    import aaa.bbb.ccc.fieldslist.FieldsList;
    import java.io.*;
    import java.net.URI;
    import javax.ws.rs.core.*;
    import javax.xml.bind.JAXBException;
    import org.apache.commons.io.IOUtils;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;

    @Stateless
    @Path("/fieldsList")
    public class Testws {

        private static final Logger LOG = LogManager.getLogger(Testws.class);

        public Testws() {
        }

        @POST
        @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
        public Response post(@Context UriInfo uriInfo, @Context javax.servlet.http.HttpServletRequest request, FieldsList fieldsList) throws IOException, JAXBException, Exception {
            try {

                //...returns empty string...
                String s = IOUtils.toString(request.getInputStream(), "UTF-8");

                LOG.info("message string from request.getInputStream()=" + s); <==  empty string...
            } catch (Exception e) {
                e.printStackTrace();
            }

            URI uri = UriBuilder.fromUri(uriInfo.getRequestUri()).build();

            Response response = Response.created(uri).build();
            return response;
        }
    }

I've tried using an interceptor (see "aroundReadFrom()" method) to manipulate the InputStream before it is used by the post() method, but, to no effect... -That is, in the REST service's post() method, the request.getInputStream() continues to yield nothing...

"aroundReadFrom()" method...

    package aaa.bbb.ccc;

    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import javax.ws.rs.WebApplicationException;
    import javax.ws.rs.ext.Provider;
    import javax.ws.rs.ext.ReaderInterceptor;
    import javax.ws.rs.ext.ReaderInterceptorContext;
    import org.apache.commons.io.IOUtils;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;

    @Provider
    public class MyReaderInterceptor implements ReaderInterceptor {

        static final Logger LOG = LogManager.getLogger(MyReaderInterceptor.class);

        @Override
        public Object aroundReadFrom(ReaderInterceptorContext ctx) throws IOException {

            try {
                InputStream is = ctx.getInputStream();
                byte[] content = IOUtils.toByteArray(is);
                is.close();

                ctx.setInputStream(new ByteArrayInputStream(content));            

                return ctx.proceed();            
            } catch (IOException | WebApplicationException e) {
                e.printStackTrace();
            }

            return null;
        }
    }

Here is the test xml message...:

    <?xml version="1.0" encoding="UTF-8"?>
    <FieldsList xmlns="http://aaa.bbb.ccc.ws/testws">
        <Fields>
            <FieldA>fieldA_value</FieldA>
            <FieldB>fieldB_value</FieldB>
            <FieldC>fieldC_value</FieldC>
        </Fields>
    </FieldsList>

Here is the schema:

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema 
        targetNamespace="http://aaa.bbb.ccc.ws/testws"
        attributeFormDefault="unqualified"
        elementFormDefault="qualified"
        xmlns:tw="http://aaa.bbb.ccc.ws/testws"
        xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xs:complexType name="FieldsType">
            <xs:all>
                <xs:element name="FieldA" type="xs:string" minOccurs="0" />            
                <xs:element name="FieldB" type="xs:string" minOccurs="0" />            
                <xs:element name="FieldC" type="xs:string" minOccurs="0" />                                                 
            </xs:all>
        </xs:complexType>
        <xs:element name="FieldsList">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="Fields" type="tw:FieldsType" minOccurs="0" maxOccurs="unbounded" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    </xs:schema>

UPDATE:

Within the post() method I've only been able to reconstruct message string using this technique...

    StringWriter sw = new StringWriter();
    JAXBContext.newInstance(FieldsList.class).createMarshaller().marshal(fieldsList, sw);
    System.out.println("posted xml string=" + sw.toString());

...However, this would not help if the same data is posted in JSON format. To clarify, it will reconstruct the JSON post message as an XML string rather than the original JSON string

Again, I what I'm trying to do is access the original posted XML/JSON message string within the post() method


Solution

  • Solution using Request Attribute - Tested work 100%

    @Provider
    public class ContainerRequestFilterImpl implements ContainerRequestFilter {
    
        @Context
        private HttpServletRequest request;
    
        @Override
        public void filter(ContainerRequestContext ctx) throws IOException {
    
            InputStream is = ctx.getEntityStream();
            byte[] content = IOUtils.toByteArray(is);
    
            // Store content as Request Attribute
            request.setAttribute("content", new String(content, "UTF-8"));
    
            ctx.setEntityStream(new ByteArrayInputStream(content));
        }
    }
    
    
    AND
    
    @POST
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Response post(@Context UriInfo uriInfo, @Context HttpServletRequest request, FieldsList fieldsList) throws IOException, JAXBException, Exception {
        try {
            String s = request.getAttribute("postContent");
            LOG.info("Post content: " + s);
    
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    I think you can use ReaderInterceptor instead of ContainerRequestFilter. It should work too.