How to use POJO class for rest WS with CXF

2019-08-09 08:08发布

问题:

I am trying to return POJO class reference to client in rest WS(CXF 3.1.2) as below,

Service method declaration :

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes({"application/xml", MediaType.TEXT_PLAIN})
    @Path("/agentLogout")
    public ResponseEvent agentLogout(String ext) {
    ResponseEvent response= new ResponseEvent();
    response.setDn(ext);
    return response;
    }

Client Code:

    WebClient client = WebClient.create(REST_URI);    
    client.path("agentLogout").accept(MediaType.APPLICATION_JSON);
    Response agentLogoutResponse = client.post("3101");
    String responseStr=agentLogoutResponse.readEntity();

POJO:

    public class ResponseEvent {
    private String dn;
    public String getDn() {
    return dn;
    }
    public void setDn(String ext) {
    this.dn=ext;
    }
    }

Question:

  1. how i can retrieve/access the returned reference in client code ?

    String responseStr=agentLogoutResponse.readEntity(); // Do i need to create the pojo class/interface in client code too..?

  2. Whether this POJO reference rendered as JSON in CXF? if so how we can use it in client code?

Thanks,

回答1:

1) You might want to have MessageBodyWriter<ResponseEvent> on server side. You might need to register this @Provider. This would enable your POJO to be written to server's output stream. For instance (not tested):

@Provider
class ResponseEventWriter implements MessageBodyWriter<ResponseEvent> {
    @Override
    public boolean isWriteable(Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return true;
    }
    @Override
    public long getSize(ResponseEvent t, Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return t.getDn().length();
    }
    @Override
    public void writeTo(ResponseEvent t, Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType,
        MultivaluedMap<String, Object> httpHeaders,
        OutputStream entityStream) throws IOException,
        WebApplicationException {
        entityStream.write(t.getDn().getBytes());
    }
}

2) You need to have MessageBodyReader<ResponseEvent> on client side. For instance:

@Provider
public class ResponseEventReader implements MessageBodyReader<ResponseEvent> {
    @Override
    public boolean isReadable(Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return true;
    }
    @Override
    public ResponseEvent readFrom(Class<ResponseEvent> type, Type    genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
        throws IOException, WebApplicationException {
        StringBuilder sb = new StringBuilder();
        try(InputStreamReader isr = new InputStreamReader(entityStream)) {
            char[] c = new char[1];
            while (isr.read(c) != -1)
               sb.append(c);
        } catch (IOException e) {
            //do something
        }
        ResponseEvent event = new ResponseEvent();
        event.setDn(sb.toString());
        return event;
    }
}

You need to register this provider on client side. You do this for instance using JAXRS client, instead of cxf client (available since cxf 3), for instance with something like:

    Client client = ClientBuilder.newClient();
    client = client.register(ResponseEventReader.class);
    Response agentLogoutResponse = client.target(/*whateveryourpath + */ "agentLogout")
            .request().accept(MediaType.APPLICATION_JSON)
            .buildPost(Entity.text("1301")).invoke();

Then you can read your entity as

ResponseEvent event = agentLogoutResponse.readEntity(ResponseEvent.class);


回答2:

Say You have created interface(It could be auto generated using WADL2Java plugin) at client side.

public interface MyService{

 @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes({"application/xml", MediaType.TEXT_PLAIN})
    @Path("/agentLogout")
    public ResponseEvent agentLogout(String ext);

}

Create A singleton Class and Create instance of Service

public class CxfRestSingleton {

    public static GenService obj;

    public static GenService getInstance() {

        if (obj == null) {
            obj = JAXRSClientFactory.create("http://localhost:8080/api/hello", MyService.class, Arrays.asList(new JacksonJaxbJsonProvider()));
        }
        return obj;
    }

}

**NOTE:**Here I'm using Jackson JAXB JSON proivder, which can marshal/unmarshal both xml and JSON you could use your choice of provider. An Jackson provider is not part of cxf so you need to include these dependency separately

And the You could access your pojo class as shown Below

CxfRestSingleton.getInstance().agentLogout(12345).getDn();