问题
I implemented a simple Android Restlet server demo that can provide the sensor values to a Restlet client. However, when the restlet client receives the object, it is null. I suppose there is not much missing because the transaction shows that it was done correctly in the server logcat:
D/SensorTemperature: GET temperature: 20.0
W/System.err: 2015-10-23 20:28:37 192.168.2.129 - - 8080 GET /sensors/temperature - 200 - 0 198 http://192.168.2.94:8080 Restlet-Framework/2.3.5 -
And on the client side, the ClientResource prints (logcat):
ClientProxy for resource: GET http://192.168.2.94:8080/sensors/temperature HTTP/1.1 => HTTP/1.1 - OK (200) - The request has succeeded
Moreover, the browser interface works as expected when using the restlet request/response (see "/test" below: ).
I followed the official Restlet tutorial: http://restlet.com/technical-resources/restlet-framework/guide/2.2/introduction/first-steps/first-application
git: https://github.com/restlet/restlet-tutorial/tree/master/modules/org.restlet.tutorial.webapi/src/main/java/org/restlet/tutorial
Along with this example: http://maxrohde.com/2011/09/02/restlet-quickstart/
The code:
Here is the restlet server:
public class ServerFactory {
static {
// Get NIO engines, instead of defaults
Engine.getInstance().getRegisteredServers().add(new HttpsServerHelper(null));
Engine.getInstance().getRegisteredServers().add(new HttpServerHelper(null));
// Engine.setLogLevel(Level.FINEST);
Engine.getInstance().getRegisteredConverters().add(new JacksonConverter());
}
private static Restlet restlet = new Restlet() {
@Override
public void handle(Request request, Response response) {
Date date = new Date();
float temp = WSDataProvider.getInstance().getTemperature();
response.setEntity("Hello World!\nTime: " + date + "\nTemp: " + temp, MediaType.TEXT_PLAIN);
}
};
public static Server createServer(int port, final String rootUri) {
Component component = new Component();
Server server = component.getServers().add(Protocol.HTTP, port);
// Attach the sample application.
component.getDefaultHost().attach("/test", restlet);
SensorsApplication sensorsApp = new SensorsApplication();
component.getDefaultHost().attach("/sensors", sensorsApp);
return server; // server.start() and server.stop() called via Android buttons
}
}
And the client:
Thread thread = new Thread(new Runnable(){
@Override
public void run() {
try {
Engine.getInstance().getRegisteredConverters().add(new JacksonConverter());
// Initialize the resource proxy.
final ClientResource cr = new ClientResource("http://192.168.2.94:8080/sensors/temperature");
final SensorResource resource = cr.wrap(SensorResource.class);
// Get the remote temperature sensor
final SensorBase sensorBase = resource.retrieve();
if (sensorBase != null)
Log.e(TAG, "sensorBase.getData() = " + sensorBase.data);
else
Log.e(TAG, "sensorBase is null !!");
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
SensorBase class: (client + server)
public class SensorBase implements Serializable {
private static final long serialVersionUID = 1L;
public static final int TYPE_UNKNOWN = 0;
public static final int TYPE_TEMPERATURE = 1;
public static final int TYPE_HUMIDITY = 2;
public int type;
public float data;
public SensorBase(final int type) {
super();
this.type = type;
}
}
SensorResource class: (client + server)
public interface SensorResource {
@Get
public SensorBase retrieve();
}
SensorTemperature class:
public class SensorTemperature extends ServerResource implements SensorResource {
private static final String TAG = "SensorTemperature";
private static volatile SensorBase sensorBase = new SensorBase(SensorBase.TYPE_TEMPERATURE);
public SensorBase retrieve() {
float temp = WSDataProvider.getInstance().getTemperature();
Log.d(TAG, "GET temperature: " + temp);
sensorBase.data = temp;
return sensorBase;
}
}
SensorsApplication class:
public class SensorsApplication extends Application {
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach("/temperature", SensorTemperature.class);
//TODO add more sensors
return router;
}
}
Update
I have fixed the null pointer exception by including the jackson jar file com.fasterxml.jackson.core.jar
. The class not found error was only shown when the Restlet client was using Engine.setLogLevel(Level.FINEST);
!
However, I get the following Exception from the client now:
org.restlet.resource.ResourceException: Unprocessable Entity (422) - The server understands the content type of the request entity and the syntax of the request entity is correct but was unable to process the contained instructions
Thanks !
回答1:
In fact, it's not a problem from Restlet but from Jackson. If you have a look at the root cause of the 422 error, you'll see this:
Caused by: com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class SensorBase]: can not instantiate from JSON object (need to add/enable type information?)
at [Source: sun.nio.ch.ChannelInputStream@1eb0d79; line: 1, column: 2]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1071)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:264)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:124)
at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1269)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:864)
at org.restlet.ext.jackson.JacksonRepresentation.getObject(JacksonRepresentation.java:299)
at org.restlet.ext.jackson.JacksonConverter.toObject(JacksonConverter.java:208)
at org.restlet.service.ConverterService.toObject(ConverterService.java:229)
at org.restlet.resource.Resource.toObject(Resource.java:889)
... 3 more
The deserialization of your object SensorBase
by Jackson requires an empty constructor in this class:
public class SensorBase implements Serializable {
private static final long serialVersionUID = 1L;
public static final int TYPE_UNKNOWN = 0;
public static final int TYPE_TEMPERATURE = 1;
public static final int TYPE_HUMIDITY = 2;
public int type;
public float data;
public SensorBase() {
}
public SensorBase(final int type) {
super();
this.type = type;
}
(...)
}
Hope it helps you, Thierry
来源:https://stackoverflow.com/questions/33311268/restlet-server-on-android-client-is-getting-a-null-object