I am trying to implement a RESTful web service client using Jersey/JAX-RS:
public class MyClient implements Closeable {
private Client client;
private FizzResource fizzResource;
// Several other resources omitted for brevity.
// Ctor, getters and setters, etc.
#Override
public void close() throws Exception {
client.destroy();
client.getExecutorService().shutdown();
}
}
public class FizzResource {
private Client client;
public Fizz saveFizz(Fizz fizz) {
WebResource webResource = client.resource("whatever");
ClientResponse response = webResource.accept(???).post(???);
if(response.getStatus() != 200) {
// do something...
} else {
// do something else...
}
}
}
My problem is that I do not want to work with JSON; instead I want to work directly with my entities (e.g. Fizz). I would like to use Jackson to automagically do the serialization between JSON and my entities (without me having to explicitly do the conversion inside each method), but I'm not seeing how this is possible/doable. Ideally my saveFizz method might look like:
public Fizz saveFizz(Fizz fizz) {
WebResource webResource = client.resource("whatever");
ClientResponse response = webResource.accept("application/json").post(fizz);
if(response.getStatus() != 200) {
throw new RuntimeException("Errors are bad, mkay?");
}
Fizz fizz = response.extractSomehow();
return fizz;
}
Assume my Fizz class is already annotated with the correct Jackson annotations (JsonProperty, etc.).
Any ideas?
You're using Jersey 1.x, so have a look at the user guide for JSON/POJO support
First thing: We need to make sure you have the jersey-json module
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>${jersey-version}</version>
</dependency>
This module will have the needed MessageBodyReader and MessageBodyWriter that will read and write you POJOs to and from JSON
Second thing: We need to make sure we enable the POJO mapping support feature. Both with the server/application and with the client
Server with web.xml
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
Server programmatic
public class MyApplication extends PackagesResourceConfig {
public MyApplication() {
getFeatures()..put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
}
}
See other Deployment Options
Client Config
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING,
Boolean.TRUE);
Client client = Client.create(clientConfig);
Third thing: We just need to make sure our resource method are annotated properly and we make the client call properly (to allow the correct writers/readers to be discovered).
For methods accepting JSON, it should annotated with #Consumed("application/json") and if the method also produces a response in JSON, it should also be annotated with #Produces("application/json"). So it depends on the semantics of your method, which annotations to include, it could be one or both.
For the client, as long as we have to correct configuration, extracting the Java Object, is just a matter of calling a getXxx with the Java type.
public void testGetFizz() {
// Directly extact
Fizz fizz = r.path("fizz").accept("application/json").get(Fizz.class);
System.out.println(fizz);
// Extract from ClientResponse
ClientResponse response = r.path("fizz").
accept("application/json").get(ClientResponse.class);
Fizz fizz1 = response.getEntity(Fizz.class);
System.out.println(fizz1);
}
Here are other pieces of code I used for my test
#Path("/fizz")
public class FizzResource {
#POST
#Consumes("application/json")
public Response postFizz(Fizz fizz) {
System.out.println("==== Created Fizz ===");
System.out.println(fizz);
System.out.println("=====================");
return Response.created(null).build();
}
#GET
#Produces("application/json")
public Response getFizz() {
Fizz fizz = new Fizz(1, "fizz");
return Response.ok(fizz).build();
}
}
Server config
ResourceConfig resourceConfig = new PackagesResourceConfig("test.json.pojo");
resourceConfig.getFeatures().put(
JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
Client config
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING,
Boolean.TRUE);
Client client = Client.create(clientConfig);
r = client.resource(Main.BASE_URI);
// r = WebResource
You could use Jackson's ObjectMapper:
final ObjectMapper mapper = new ObjectMapper();
mapper.readValue(response.getEntity(String.class), Fizz.class);
As long as Fizz is correctly annotated, this should do what you are looking for.
There are other options as well, which usually involve implementing custom providers.
If you include (supposing you use maven)
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>${jersey.version}</version>
</dependency>
then you will have automagically conversion without setting up anything. You can write functions like:
#POST
#Consumes(APPLICATION_JSON)
#Produces(APPLICATION_JSON)
public Activity createActivity(#Valid Activity activity) {
return activityDAO.createActivity(vuser,activity);
}
Related
I build a simple Jersey rest server, that handle a simple service.
In main class that start the server, i read properties file that i want to
provide/pass to jax handler classes.
The server works, i just have to make a way to share config parameters of
main class with handler of requests.
How i can do this ?
The code of main, where i read de properties file one time only:
...
public HashMap resources;
// this start the listener jersey server...
String host="http://localhost/";
int port = 9998;
URI baseUri = UriBuilder.fromUri(host).port(port).build();
ResourceConfig config = new ResourceConfig();
config.packages(true, "br.com.myserver");
config.register(MyHandler.class);
// I WANT TO ACCESS/SHARE THIS WITH THE HANDLER -> MyHandler.class
resources.put("host_user","bla bla bla");
HttpServer server = JdkHttpServerFactory.createHttpServer(baseUri, config);
System.out.println("--Press Enter to STOP the server--");
System.in.read();
Server.stop(0);
System.out.println("Server stoped!");
...
The code of MyHandler, where i want to access main properties:
#Path("myapp")
public class MyHandler
{
#POST #Path("/testep")
#Consumes("application/json")
#Produces("text/plain")
public String action1(#Context Request request, String json)
{
// HERE I WANT TO ACCESS THE RESOUCES HASHMAP OF MAIN HERE
// (how get main handler here).resources.get("host_user");
// maybe access main class, or something like
// the intention is to avoid the read of config at all requests here
System.out.println("received event:" + json);
return "event received " + json;
}
}
Any ideas will be apreciated, thanks.
Any properties you configure the ResourceConfig using it's property(key, value), will be accessible through the Configuration interface which you can inject into your resource class.
ResourceConfig config = new ResourceConfig();
config.property("host_user","bla bla bla");
...
#Path("myapp")
public class MyHandler
{
#Context
Configuration configuration;
public String action1(#Context Request request, String json) {
Map<String, Object> props = configuration.getProperties();
}
}
See Also:
Configuration Properties with Jersey for some other ideas.
I am making a POST request which sends a JSON. The Controller picks up the JSON, processes the JSON and I want the controller to return some data in XML format.
How can I do that with a POST request?
#RequestMapping( value = Controller.RESOURCE_PATH + ".xml", headers = "Accept=application/json", produces = "*/*" )
public String exportXml( #RequestBody String requestJson ) throws IOException
{
JSONObject json = JSONObject.fromObject( requestJson );
Option option = new Option();
option.processJson( json );
return "";
}
There are many ways to achieve this. One is to use MarshallingView and XStreamMarshaller
Firstly add following jars to your classpath (maven dependencies):
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.4</version>
</dependency>
Then configure a Marshaller on your spring xml configuration
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"/>
Assuming you have following bean you want to Marshal (ie: display as XML)
public class MyMessage {
private String message;
// getters & setters
}
In your controller class inject org.springframework.oxm.Marshaller and have your handler method return a MarshallingView like this:
#Controller
public class MyController {
#Autowired private Marshaller marshaller;
#RequestMapping("/helloxml")
public MarshallingView helloxml(Model model) {
MyMessage msg = new MyMessage();
msg.setMessage("hello world");
model.addAttribute("msg", msg);
MarshallingView marshallingView = new MarshallingView(marshaller);
marshallingView.setModelKey("msg"); // set what model attribute to display as xml
return marshallingView;
}
}
The above setup will give you xml like this when /helloxml is requested
<com.gerrydevstory.xmlview.MyMessage>
<message>hello world</message>
</com.gerrydevstory.xmlview.MyMessage>
Of course this isn't a very good setup if you deal with many XML marshalling. You should leverage view resolvers configuration in this case.
Also the name of XML element can be aliased too to shorten it. Check out the XStream documentation
Finally, keep in mind XStream is just one of many marshaller supported by Spring, also consider JAXB, Castor, Jibx etc.
I would like to add an ExceptionMapper to CXF (2.6.1) which not only communicates the Response code, but also ships the exception in the payload format (I'm using JSON for now).
#Provider
public class CustomExceptionMapper
implements
ExceptionMapper<MyException>
{
...
#Override
public Response toResponse(MyException mex)
{
//I need something here which can convert mex object to JSON and ship it in response
// I want this to be de-serialized on client
//the following returns the status code
return Response.status(Response.Status.BAD_REQUEST).build();
}
...
}
Is there a way to do this ?
You may need to use #Produces to serialize your object to JSON like:
#Produces(MediaType.APPLICATION_JSON)
And then return Response.ok().entity(OBJECT).build();
The way that you can test your service:
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource service = client.resource(getBaseURI());
ClientResponse response = service.path(ADDRESS).type("application/json").get(ClientResponse.class);
String s = response.getEntity(String.class);
System.out.println(s);
private static URI getBaseURI() {
return UriBuilder.fromUri(SERVER ADDRESS).build();
}
I'm using Jersey 1.11 and trying to model both POST and DELETE methods in my RESTful resource.
The problem I am encountering is in my unit tests. I cannot use the delete method of WebResource and still expect a ContentResponse instance (there are workarounds but since I will at some point use a Rails front end I'd rather sort this out now). So I'm trying to use POST with the PostReplaceFilter and have variously tried submitting form and query _method parameters (set to DELETE) as well as the X-HTTP-Method-Override header.
I am configuring the PostReplaceFilter in web.xml as such:
<servlet-name>SomeName</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>com.sun.jersey.api.container.filter.PostReplaceFilter</param-value>
</init-param>
My REST resource looks like this:
#Controller
#Scope("prototype")
#Path("{apiVersion}/push/")
public class Push extends BaseRequest {
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.APPLICATION_JSON)
#Path("registration")
public Response create(#FormParam(REGISTRATION_ID_FIELD) String registrationId) throws Exception {
/* omitted; this method ends in a 200 OK */
}
#DELETE
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Path("registration")
public Response destroy(#FormParam(REGISTRATION_ID_FIELD) String registrationId) throws Exception {
/* omitted; this method ends in a 204 NO CONTENT */
}
My unit test always invokes the first create method as if the PostReplaceFilter is having no effect:
private WebResource.Builder prepare(String path, String method) {
return resource().path(path)
.queryParam("_method", method)
.cookie(new Cookie(Report.DEVICE_COOKIE_NAME, TEST_DEVICE_ID))
.cookie(new Cookie(Report.TOKEN_COOKIE_NAME, TEST_TOKEN));
}
#Test
public void destroyShouldEliminateAnyPushRegistrationAndTokenForADevice() throws Exception {
// mocks are setup here
MultivaluedMap<String, String> formData = createFormData(registrationId);
ClientResponse response = prepare("/1.0/push/registration", "DELETE")
.header("X-HTTP-Method-Override", "DELETE")
.post(ClientResponse.class, formData);
assertThat(response.getStatus(), is(204));
// mocks are verified here
}
I know the first method is being invoked because create returns a 200 while the destroy method returns 204 and the assertThat line is failing due to a 200 status (additionally my expected methods on my mocks are not being invoked).
My unit test inherits from JerseyTest and uses the Grizzly2 Web Container.
My co-worker pointed out that the web.xml is not actually used as part of the test stack. Instead I needed to initialize the filters programmatically; in our case our base test inherited from JerseyTest and overwrote the configure method:
#Override
protected AppDescriptor configure() {
return new WebAppDescriptor.Builder("com.yourproject.blah")
.initParam("com.sun.jersey.spi.container.ContainerRequestFilters",
"com.sun.jersey.api.container.filter.PostReplaceFilter")
.servletClass(SpringServlet.class)
.build();
}
Im new to Rest web services and say Ive created this web service using Netbeans
#Path("browse")
#Stateless
public class ArticleBrowseResource {
#EJB
private ArticleSearcherLocal ejbRef;
#GET
#Produces(MediaType.APPLICATION_XML)
public List<Article> browse(#DefaultValue("") #QueryParam("username") String username,#QueryParam("sd") String sd) {
// convert sd string to date
List<Article> articles = ejbRef.search(username, date);
return articles;
}
}
where Article is an entity which is anotated with #XmlRootElement
Now how am I supossed to retreive this list of articles in my client which for simplicity lets just say it is a java standard application? In SOAP web services I know that these objects are automatically generated but not in Rest.
This is the client class generated for this service by Netbeans
public class ArticleBrowseClient {
private WebResource webResource;
private Client client;
private static final String BASE_URI = "http://localhost:8080/cityblog/rest";
public ArticleBrowseClient() {
com.sun.jersey.api.client.config.ClientConfig config = new com.sun.jersey.api.client.config.DefaultClientConfig();
client = Client.create(config);
webResource = client.resource(BASE_URI).path("browse");
}
public <T> T browse(Class<T> responseType, String username, String sd) throws UniformInterfaceException {
WebResource resource = webResource;
if (username != null) {
resource = resource.queryParam("username", username);
}
if (sd != null) {
resource = resource.queryParam("sd", sd);
}
return resource.accept(javax.ws.rs.core.MediaType.APPLICATION_XML).get(responseType);
}
public void close() {
client.destroy();
}
}
What is the best and simplest way to resolve this issue?
Any help is appreciated and
thx in advance
Please try fewer code generation and more understanding of what you are actually doing. On the server, you generate a XML message with help of JAXB. On the client side, you can consume this XML with a programming language and library you like. Just use tools like curl to see what is going actually over "the wire". Your generated client site looks fully reasonable. You just need your Article class from the server side on the client side. The generated code uses Jersey which can read XML messages per JAXB per default. So just drop your server side Article class in your client side classpath and use it. But please also have a look at the wire level protocol to understand the portability of your REST API.