I want to realise a restlet client for my jersey service. I know that the service works, because I already developed a jersey client for it. But it seems like jersey and rest get problems to communicate with each other.
Jersey Service
Ressource:
@Path("/object")
@RolesAllowed({"admin", "user"})
public class ObjectResourceBean implements ObjectResourceIF {
@POST
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response postObject(JAXBElement<ObjectDTO> object) {
ObjectDTO c = object.getValue();
String generatedId = generateID();
c.setObjectId(generatedId);
c.setOwner(sec.getUserPrincipal().getName());
return postAndGetResponse(c);
}
private Response postAndGetResponse(ObjectDTO object) {
Response res;
res = Response.created(UriBuilder.fromUri(uriInfo.getAbsolutePath() + "/" + object.getObjectId()).build()).entity(object).build();
ObjectDAO.instance.getObjectDao().put(object.getObjectId(), object);
System.out.println("Response: " + res);
}
return res;
}
Restlet Wrap Interface:
@Path("/object")
@RolesAllowed({"admin", "user"})
public interface ObjectResourceIF {
@Post
public Response postObject(JAXBElement<ObjectDTO> object);
}
Already here I can't work with Jersey Annotations like @POST. I have to use @Post from Restlet Framework. But thats not the real problem.
Restlet Client
ClientResource service = new ClientResource("http://localhost:8080/com-project-core/rest");
service.setChallengeResponse(ChallengeScheme.HTTP_BASIC, "admin", "xxx");
service.getReference().addSegment("object");
// *create object*
JAXBElement<ObjectDTO> object = new JAXBElement<ObjectDTO>(new QName("object"), ObjectDTO.class, objectOne);
ObjectResourceIF objectResource = service.wrap(ObjectResourceIF.class);
List<Preference<MediaType>> acceptedMediaTypes = new ArrayList<Preference<MediaType>>();
acceptedMediaTypes.add(new Preference(MediaType.APPLICATION_JSON));
service.getClientInfo().setAcceptedMediaTypes(acceptedMediaTypes);
Response res = objectResource.postObject(object);
I just get a Internal Server Error (500).
Internal Server Error (500) - The server encountered an unexpected condition which prevented it from fulfilling the request
at org.restlet.resource.UniformResource.toObject(UniformResource.java:649)
at org.restlet.resource.ClientResource$1.invoke(ClientResource.java:1669)
at $Proxy12.postObject(Unknown Source)
at com.project.restlet.RestletConnectedTest.postObject(RestletConnectedTest.java:108)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: com.thoughtworks.xstream.mapper.CannotResolveClassException: objectId : objectId
at com.thoughtworks.xstream.mapper.DefaultMapper.realClass(DefaultMapper.java:68)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.DynamicProxyMapper.realClass(DynamicProxyMapper.java:71)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.PackageAliasingMapper.realClass(PackageAliasingMapper.java:88)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.ClassAliasingMapper.realClass(ClassAliasingMapper.java:86)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.ArrayMapper.realClass(ArrayMapper.java:96)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:38)
at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:52)
at com.thoughtworks.xstream.core.util.HierarchicalStreams.readClassType(HierarchicalStreams.java:29)
at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:136)
at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:33)
at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:923)
at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:909)
at com.thoughtworks.xstream.XStream.fromXML(XStream.java:861)
at org.restlet.ext.xstream.XstreamRepresentation.getObject(XstreamRepresentation.java:166)
at org.restlet.ext.xstream.XstreamConverter.toObject(XstreamConverter.java:200)
at org.restlet.service.ConverterService.toObject(ConverterService.java:161)
at org.restlet.resource.UniformResource.toObject(UniformResource.java:647)
... 27 more
On Server Side the request gets done to
return res;
When I try it without wrapping the Interface with
service.post(object, MediaType.APPLICATION_JSON).write(System.out);
it works!
As I said, the Jersey Client works as well.
Maybe the transfered Class ObjectDTO is useful for the solution:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class ObjectDTO implements Serializable{
/**
*
*/
private static final long serialVersionUID = -8545841080597549468L;
@XmlElement(name="objectId")
private String objectId;
@XmlElement(name="owner")
private String owner;
@XmlElement(name="objectName")
private String objectName;
public ObjectDTO() {
}
public ObjectDTO(String objectName) {
this.objectName = objectName;
}
public String getObjectId() {
return objectId;
}
public void setObjectId(String objectId) {
this.objectId = objectId;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
}
I tried it without serializable as well. Also without @XmlElement and @XmlAccessorType(XmlAccessType.FIELD). As well I tried it with
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
annotation at the interface.
We had the same Problem with a Jersey server and Restlet client, but with a get Request. With a Restlet client to Restlet server communication we hat no problems. So the only difference we could see was, that Restlet server implementation wraps the full qualified class name of the marshalled POJO as a JSON Object around the JSON Object which contains the POJOs payload.
package com.xyz;
public class ExamplePojo implements Serializable{
private static final long serialVersionUID = 1L;
private String var;
public ExamplePojo(){
}
//...
}
Resulting json string on Restlet server:
{"com.xyz.ExamplePojo":{"var":"10:23:53 30.10.2012"}}
but Jersey server in its default implementation if producing MediaType.APPLICATION_JSON doesn't. Resulting json string on Jersey server:
{"var":"10:23:53 30.10.2012"}
The problem was, that if we attempt communication with the Jersey server via a Restlet client, we got the same error like in your stacktrace with the root cause:
Caused by: com.thoughtworks.xstream.mapper.CannotResolveClassException: var : var
The soulution in our case was, that we explicitly had to define the JacksonConverter if using a Restlet client with a Jersey server implementation.
So we put the libraries from the Restlet zip on our build path of the client project
org.restlet.ext.jackson.jar
org.codehaus.jackson.core.jar
org.codehaus.jackson.mapper.jar
(interface of the resource)
public interface PojoResource {
@Get("json")
public ExamplePojo retrieve();
}
and then we add explicit the JacksonConverter to the registered converters
public ExamplePojo doRequest(){
ClientResource cr = new ClientResource( url );
// this is essential for Restlet-Jersey marshalling
Engine.getInstance().getRegisteredConverters().add( 0, new JacksonConverter() );
PojoResource resource = cr.wrap( PojoResource.class );
return resource.retrieve();
}
and everything worked fine.
Note that an explicit call to
Engine.getInstance().getRegisteredConverters().add( 0, new JacksonConverter() );
on a Reslet-Client to Restlet-Server communication did not work, but led to an
org.codehaus.jackson.map.JsonMappingException
because of the wrapping JSON Object 'com.xyz.ExamplePojo' as an unrecognized field, which must be marked as ignoreable.
So in your case it seems that Restlet doesn't find a converter for the unmarshalling either, so try to specify an explicit one.