Ticket #515 (closed Enhancement: fixed)

Opened 7 years ago

Last modified 7 years ago

allow plugins to build-up the viewmodel (enrich the application model) directly

Reported by: Malte Owned by: jri
Priority: Minor Milestone:
Component: DeepaMehta Standard Distribution Version: 4.1.2
Keywords: Cc:
Complexity: 3 Area: Application Framework / API
Module: deepamehta-core


This is issue asks about how to enrich server-side response-objects about custom child-types which are not part of the application model-definition (before sending out the response) to the client.

Often it is so that, the actual viewmodel differs from the application model but so it is more efficient to build up the applications viewmodel on server-side.

Probably this should be done via implementing new (view-)model classes directly in my server-side plugin and let those classes implement JSON-serializable, or?

Thanks for your help.

Change History

comment:1 follow-up: ↓ 5 Changed 7 years ago by jri

Yes, you can enrich any topic (association, type, ...) on-the-fly, that is before it is send to the client. You'r plugin does so by implementing a PreSendTopicListener (resp. one of the other PreSend?... listeners).

import de.deepamehta.core.service.event.PreSendTopicListener;

public class MyPlugin ... implements PreSendTopicListener {

    public void preSendTopic(Topic topic, ClientState clientState) {
        // enrich
        CompositeValueModel comp = topic.getCompositeValue().getModel();
        comp.put(fieldUri1, aSimpleValue)
        comp.put(fieldUri2, aTopicModel)
        comp.add(fieldUri3, aTopicModel)

As you see, enrichment takes place by putting arbitrary values in the topic's CompositeValueModel?. You can put simple values (String, Integer, Long, Double, Boolean) or entire topic models (simple or composite), and you can add topic models to multiple-value fields. See the CompositeValueModel? API.

The field URIs are contrived. There must be *no* corresponding (child) type definition. At client-side you access the enrichment values via the topic.composite property and these field URIs.

Note: enrichment takes place in the CompositeValueModel, not in the (DB-attached) CompositeValue object, as this would try to write the enrichment values to the DB (what is not what you want and might fail).

The PreSendTopic? events is fired for all Topic objects (as well as all Topic-subclass objects) which are

  • returned by a resource method
  • contained in any Iterable which is returned by a resource method
  • contained in a Directives object (in a UPDATE_TOPIC directive in particular) which is returned by a resource method

So, by using the PreSend?... listeners you're not required to implement any custom model classes, and you're not required to care about serialization (implementing the JSONEnabled interface). Instead you enrich the topic's composite value (model) on-the-fly by arbitrary values (which are serialized along with regular composite value serialization then).

For examples see the Geomaps, Time, and Kiezatlas plugins which enrich topics respectively with geo-coordinates, timestamps, and facets. See also how the AccessControl? plugin enriches types by permission information.

comment:2 Changed 7 years ago by jri

Thanks for the phone call! I understand, you want interfere with the Geomap object that is send as a response by the Geomaps plugin. And yes, you can't use the PreSendTopicListener here, as the Geomap model class is not a Topic (according to the class hierarchy), so the PreSendTopic event is not fired.

In this case you can implement a ServiceResponseFilterListener. That event is fired exactly once per response and allows your plugin to interfere with the response before it is send to the client. (Note: in contrast the PreSendTopic event is fired multiple times if the response is a topic collection.) By implementing a ServiceResponseFilterListener you can modify the response headers and/or the response entity (as contained in the message body).

import de.deepamehta.core.service.event.ServiceResponseFilterListener;
import com.sun.jersey.spi.container.ContainerResponse;

public class MyPlugin ... implements ServiceResponseFilterListener {

    public void serviceResponseFilter(ContainerResponse response) {
        Object entity = response.getEntity();
        if (entity instanceof Geomap) {
            Geomap geomap = (Geomap) entity;
            // interfere with the geomap here

As examples see the Time plugin and the Caching plugin.

Tell me if you want me to equip the Geomap class with a public topic iterator.

comment:3 follow-up: ↓ 4 Changed 7 years ago by jri

Note: the ServiceResponseFilterListener? was introduced in DM 4.1.1
DM 4.1 provides no comparable mechanism.

comment:4 in reply to: ↑ 3 Changed 7 years ago by jri

Replying to jri:

DM 4.1 provides no comparable mechanism.

Well, in DM 4.1 you could register a Servlet Filter with the PluginActivator?'s registerFilter() method. However, that would be quite cumbersome.

comment:5 in reply to: ↑ 1 Changed 7 years ago by Malte

Replying to jri:

Yes, you can enrich any topic (association, type, ...) on-the-fly, that is before it is send to the client. You'r plugin does so by implementing a PreSendTopicListener (resp. one of the other PreSend?... listeners).

Yes, this case is about dealing with a collection (which needs to be enriched) but I cannot use the PreSendListener? for my use-case since I now looked at my code and saw that I already had to implement a custom getAllTopicsByType()-Call to have some sorted resultset with (simulated) paging for a simple "by_type"-query.

And maybe that is why I cannot utilize the frameowkrs preSend-Method because on my particular Resource-Method I am returning a simple json-string (containing my sorted resultset), and that's why the preSend-Hook is not called. So for now, I will just have to enrich my ordered (json-string) response-set about some object-properties on each item and should be OK.

And yes:
For the a simple get_topic_by_id-call (wich returns single topics and not collections) your preSend-Topic-Hook is fired as expected and I can make use of that. I guess, so that everyone is able to return a sorted collection from DeepaMehta one would want to work with an ArrayList? instead of having a ResultSet? at hand, or? Do you want to open up a new issue covering that?

And yes:
In the the other use-case, talking about entire views which need to be enriched before they get transmitted: Such an iterator would be neat, if someone wants to alter a response-message-body.

I guess what we see now is that sorted result-sets and model enrichment (which is what use-case 1 asks for) is by now just possible when returning JSONArrays (as Strings) or after introducing a complete new abstraction-layer with my plugin.

Thanks for your help.

comment:6 Changed 7 years ago by jri

Meanwhile the concept of a Viewmodel Customizer is realized (#505). It allows to enrich a topicmap viewmodel on-the-fly before it is send to the client. It saves you from implementing a complete viewmodel on your own.

How do you regard the status of this ticket?

comment:7 Changed 7 years ago by Malte

  • Status changed from new to closed
  • Resolution set to fixed

To close this ticket I want to wrap it up:

0.) serviceResponseFilter() would allow to do raw tricks on my JSONArrays, but

1.) Extending generic viewmodels (e.g. plain List<TopicModel?> is already possible) and this was the way I solved the issue. Thanks for the hint on the phone, jri.

2.) Implementing custom viewmodels as POJOs (which implement JSONSerialziable) and returning a List of those seems also possible for developers of third-party applications and custom interfaces.

3.) And yes, the new features realized with #505 allow great new use-cases for applications dealing with topicmaps, geomaps and/or the dm4-webclient, but that is not related to the issue described and solved here. I used the Geomaps scenario just as an illustration of the original problem (as it was a problem of the use-case described in #550).


Note: See TracTickets for help on using tickets.