sane-city / wot-servient

W3C Web of Things implementation for Java
MIT License
30 stars 5 forks source link

Servient::destroy method doesn't reset top level Thing's forms #5

Closed Dnnd closed 4 years ago

Dnnd commented 4 years ago

Expected behavior

Thing::destroy invocation will remove all Thing's forms as its impossible to interact with.

Observed behavior

Thing::destroy invocation won't reset forms on top level of Thing Description.

Consider the following code:

public class Test {
    public static void main(String[] args) throws WotException, IOException, ExecutionException, InterruptedException {

        Config config = ConfigFactory.parseString(
                "wot.servient.client-factories = [\"city.sane.wot.binding.mqtt.MqttProtocolClientFactory\"], wot.servient.servers = [\"city.sane.wot.binding.http.HttpProtocolServer\"]");
        config = config.withFallback(ConfigFactory.load());
        Wot wot = new DefaultWot(config);

        Thing thing = new Thing.Builder().setId("example-id")
                                         .setObjectType("Thing")
                                         .addProperty("example-property", new ThingProperty.Builder()
                                                 .build())
                                         .build();
        ExposedThing exposed = wot.produce(thing).expose().get();
        exposed.destroy().get();
        wot.discover().map(Thing::toJson)
           .blockingSubscribe(json -> System.out.println("discovered thing: " + json));
    }
}

Output:

20:07:15.710 | main | INFO  | city.sane.wot.Servient | Start Servient
20:07:15.714 | main | INFO  | c.s.w.b.http.HttpProtocolServer | Starting on '0.0.0.0' port '8080'
20:07:15.865 | Thread-0 | INFO  | org.eclipse.jetty.util.log | Logging initialized @719ms to org.eclipse.jetty.util.log.Slf4jLog
20:07:15.865 | Thread-0 | INFO  | org.eclipse.jetty.util.log | Logging initialized @719ms to org.eclipse.jetty.util.log.Slf4jLog
20:07:15.928 | ForkJoinPool.commonPool-worker-5 | INFO  | c.s.w.b.mqtt.MqttProtocolSettings | MqttClient trying to connect to broker at 'tcp://localhost:1883' with client ID 'wot27330927332957'
20:07:15.975 | Thread-0 | INFO  | s.e.jetty.EmbeddedJettyServer | == Spark has ignited ...
20:07:15.976 | Thread-0 | INFO  | s.e.jetty.EmbeddedJettyServer | >> Listening on 0.0.0.0:8080
20:07:15.978 | Thread-0 | INFO  | org.eclipse.jetty.server.Server | jetty-9.4.30.v20200611; built: 2020-06-11T12:34:51.929Z; git: 271836e4c1f4612f12b7bb13ef5a92a927634b0d; jvm 11.0.8+10
20:07:15.978 | Thread-0 | INFO  | org.eclipse.jetty.server.Server | jetty-9.4.30.v20200611; built: 2020-06-11T12:34:51.929Z; git: 271836e4c1f4612f12b7bb13ef5a92a927634b0d; jvm 11.0.8+10
20:07:16.015 | Thread-0 | INFO  | org.eclipse.jetty.server.session | DefaultSessionIdManager workerName=node0
20:07:16.015 | Thread-0 | INFO  | org.eclipse.jetty.server.session | DefaultSessionIdManager workerName=node0
20:07:16.015 | Thread-0 | INFO  | org.eclipse.jetty.server.session | No SessionScavenger set, using defaults
20:07:16.015 | Thread-0 | INFO  | org.eclipse.jetty.server.session | No SessionScavenger set, using defaults
20:07:16.017 | Thread-0 | INFO  | org.eclipse.jetty.server.session | node0 Scavenging every 660000ms
20:07:16.017 | Thread-0 | INFO  | org.eclipse.jetty.server.session | node0 Scavenging every 660000ms
20:07:16.049 | Thread-0 | INFO  | o.e.jetty.server.AbstractConnector | Started ServerConnector@76a909c0{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
20:07:16.049 | Thread-0 | INFO  | o.e.jetty.server.AbstractConnector | Started ServerConnector@76a909c0{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
20:07:16.049 | Thread-0 | INFO  | org.eclipse.jetty.server.Server | Started @908ms
20:07:16.049 | Thread-0 | INFO  | org.eclipse.jetty.server.Server | Started @908ms
20:07:16.285 | ForkJoinPool.commonPool-worker-5 | INFO  | c.s.w.b.mqtt.MqttProtocolSettings | MqttClient connected to broker at 'tcp://localhost:1883'
20:07:16.396 | main | INFO  | city.sane.wot.Servient | Servient exposing 'example-id'
20:07:16.397 | main | INFO  | c.s.w.b.http.HttpProtocolServer | HttpServer on '0.0.0.0' port '8080' exposes 'example-id' at http://0.0.0.0:8080/example-id
20:07:16.420 | main | INFO  | city.sane.wot.Servient | Servient stop exposing 'ExposedThing{objectType='Thing', objectContext=null, id='example-id', title='null', titles=null, description='null', descriptions=null, properties={example-property=ExposedThingProperty{name='example-property', state=city.sane.wot.thing.property.PropertyState@60856961, objectType='null', type='string', observable=false, readOnly=false, writeOnly=false, optionalProperties={}, description='null', descriptions=null, forms=[Form{href='http://192.168.1.78:8080/example-id/properties/example-property', op=[READ_PROPERTY, WRITE_PROPERTY], subprotocol='null', contentType='application/json', optionalProperties={}}], uriVariables={}}}, actions={}, events={}, forms=[Form{href='http://192.168.1.78:8080/example-id/all/properties', op=[READ_ALL_PROPERTIES, READ_MULTIPLE_PROPERTIES], subprotocol='null', contentType='application/json', optionalProperties={}}], security=[], securityDefinitions={}, base=''}'
20:07:16.472 | main | INFO  | c.s.w.b.http.HttpProtocolServer | HttpServer on '0.0.0.0' port '8080' stop exposing 'example-id' at http://0.0.0.0:8080/example-id
discovered thing: {"id":"example-id","properties":{"example-property":{"type":"string"}},"forms":[{"href":"http://192.168.1.78:8080/example-id/all/properties","op":["readallproperty","readmultipleproperty"],"contentType":"application/json"}],"@type":"Thing"}

There are 2 problems:

  1. Destroyed thing is discovered in Servient::discoverLocal as its not deleted from Servient in Servient::destroy method. This problem is related to https://github.com/sane-city/wot-servient/issues/3.
  2. Disovered thing still have forms to perform readllproperty and readmultipleproperty operations, which is misleading, as thing has been deleted already. This is happening, because Servient::destroy method https://github.com/sane-city/wot-servient/blob/1030f1688955ee294928ac7531488b33e644682e/wot-servient/src/main/java/city/sane/wot/Servient.java#L174 won't reset top-level thing's forms.

I believe, if thing would've removed from Servient in Servient::destroy invocation, there would be no need in resetting thing's forms. Otherwise, it's trivial to perform thing.setForms(new ArrayList<>()) inside Servient::destroy invocation.