Mouhammed Soueidane
Mouhammed Soueidane

Reputation: 1066

Parsing String[] Request Parameter through REST with Apache CXF

I'm currently trying to develop a simple proof of concept for a REST application using CXF 2.6.4. I'm taking the non-spring approach. I'm using JBoss AS 7.1.1 Final as my web server.

The following is my web service class:

package restful.webservice;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import restful.entity.ControllerVersion;

@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class WebServiceRest implements WebServiceInterface
{

    @Override
    @POST
    @GET
    @Path("/getVersion")
    public ControllerVersion getVersion(String deviceID, String[] macAddresses)
    {

        return new ControllerVersion();
    }
}

ControllerVersion class:

package restful.entity;

@XmlRootElement(name = "ControllerVersion")
public class ControllerVersion {

    private String version="R1.1.0.0";

    public String getVersion() {
        return version;
    }

}

Now, when I try to make a REST call to my web service, I get the following exception on the server side:

18:51:52,913 WARNING [org.apache.cxf.jaxrs.utils.JAXRSUtils] (http--0.0.0.0-8080-1) No message body reader has been found for request class String[], ContentType : application/json.
18:51:52,927 WARNING [org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper] (http--0.0.0.0-8080-1) javax.ws.rs.WebApplicationException
        at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBody(JAXRSUtils.java:1054)
        at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameter(JAXRSUtils.java:614)
        at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameters(JAXRSUtils.java:578)
        at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:238)
        at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:89)
        at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:262)
        at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
        at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:236)
        at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:209)
        at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:154)
        at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:130)
        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:225)
        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:145)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:754)
        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:201)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
        at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368)
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877)
        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671)
        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930)
        at java.lang.Thread.run(Thread.java:662)

The libraries that I have under WEB-INF/lib are:

cxf-api-2.6.4.jar
cxf-bundle-2.6.4.jar
cxf-rt-frontend-jaxrs-2.6.4.jar
cxf-rt-frontend-jaxrs.jar
cxf-rt-transports-http-2.6.4.jar
geronimo-servlet.jar
jaxb-api-2.2.5.jar
jaxb-impl-2.2.5.1.jar
jaxb-xjc-2.2.5.1.jar
jettison-1.3.4.jar
log4j-1.2.16.jar
neethi-3.0.2.jar
xmlschema-core-2.0.3.jar

The interesting thing is that I tried to do another project, but this time in the signature of my web service method, I included a custom class. I had no problem reading the contents of the custom class' instance, nor a problem while returning it to my client.

Am I missing something?

UPDATE: I tried to add the following to my web.xml:

<init-param>
  <param-name>jaxrs.providers</param-name>
  <param-value>
      org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider
      (writeXsiType=false)
  </param-value> 
</init-param>

And added the following dependencies as well:

jackson-jaxrs-1.9.2.jar
jackson-mapper-asl-1.9.2.jar
jackson-xc-1.9.2.jar

Now I'm getting the following exception:

19:22:29,762 WARNING [org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper] (http--0.0.0.0-8080-1) javax.ws.rs.WebApplicationException: java.io.IOException: Stream closed
        at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:243)
        at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:89)
        at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:262)
        at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
        at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:236)
        at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:209)
        at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:154)
        at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:130)
        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:225)
        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:145)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:754)
        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:201)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
        at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368)
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877)
        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671)
        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930)
        at java.lang.Thread.run(Thread.java:662)
Caused by: java.io.IOException: Stream closed
        at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:377)
        at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:193)
        at java.io.FilterInputStream.read(FilterInputStream.java:116)
        at org.codehaus.jackson.impl.ByteSourceBootstrapper.ensureLoaded(ByteSourceBootstrapper.java:507)
        at org.codehaus.jackson.impl.ByteSourceBootstrapper.detectEncoding(ByteSourceBootstrapper.java:129)
        at org.codehaus.jackson.impl.ByteSourceBootstrapper.constructParser(ByteSourceBootstrapper.java:224)
        at org.codehaus.jackson.JsonFactory._createJsonParser(JsonFactory.java:785)
        at org.codehaus.jackson.JsonFactory.createJsonParser(JsonFactory.java:561)
        at org.codehaus.jackson.jaxrs.JacksonJsonProvider.readFrom(JacksonJsonProvider.java:414)
        at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBody(JAXRSUtils.java:1038)
        at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameter(JAXRSUtils.java:614)
        at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameters(JAXRSUtils.java:578)

Update 2: I tried to implement a method with a different signature, and this time, I have omitted the String array from the method:

public ControllerVersion getVersion(String deviceID)

Everything is working perfectly without the String array. What do you think I should do?

Upvotes: 2

Views: 5415

Answers (3)

Mouhammed Soueidane
Mouhammed Soueidane

Reputation: 1066

I apparently misunderstood how REST works. What I did to finally solve the problem was the following:

1- Changed my web service signature:

@POST
@Path("/getVersion")
@Produces(MediaType.APPLICATION_JSON)
@Consumes({"application/xml", "application/json", "application/x-www-form-urlencoded"})
public ControllerVersion getVersion(@FormParam("deviceID") String deviceID,@FormParam("macAddresses") String macAddresses)

2- Now, make sure that the client is sending a request with content-type (application/x-www-form-urlencoded) and that the client correctly adds form parameters same as those you defined in the @FormParam annotations.

3- Now, you can use a library such as FlexJson, to deserialize your parameters from a JSon String. For instance, I'm now sending the macAddresses as a JSon String: ["mac1","mac2"] from the client. On the server side, I convert this JSon string to a String[] using the following method:

public static String[] parseStringArrayFromJSONArray(String jsonArray)
{
    String[] stringArray = null;
    try
    {
        JSONArray array = new JSONArray(jsonArray);
        if (array != null)
        {
            stringArray = new String[array.length()];

            for (int i = 0; i < array.length(); i++)
            {
                stringArray[i] = array.getString(i);
            }
        }
    } catch (Exception e)
    {
        stringArray = null;
    }

    return stringArray;
}

I hope this will help someone out there.

Upvotes: 1

kslote1
kslote1

Reputation: 740

cxf uses jettison by default. Why do you have @GET and @POST annotations? But it say's

 java.io.IOException: Stream closed

So there is most likely an inputstream that isn't being shut down.

Upvotes: 0

Avi
Avi

Reputation: 21858

You're using @Consumes(MediaType.APPLICATION_JSON). You need to add jackson-core (and maybe some more jackson dependencies) for the conversion to work.

Upvotes: 0

Related Questions