Ticket #261 (closed Enhancement: fixed)

Opened 8 years ago

Last modified 8 years ago

Redesign server-side hook mechanism

Reported by: jri Owned by: jri
Priority: Major Milestone: Release 4.1
Component: DeepaMehta Standard Distribution Version: 4.0.11
Keywords: Cc:
Complexity: 8 Area: Application Framework / API
Module: deepamehta-core

Description

Plugins should register listener objects at the core instead of overriding dedicated methods. The core would call the registered listeners explicitly instead of iterating over all plugins installed. This should be the more efficient approach.

Note: #173 covers the same issue for the client-side.

Change History

comment:1 Changed 8 years ago by jri

  • Status changed from new to accepted

comment:2 Changed 8 years ago by dgf

please have a look at the OSGi Event Admin Service Specification (page 295) as possible implementation of a dynamic dispatch service

comment:3 Changed 8 years ago by jri

Thanks for that impulse!
I will definitely consider using the Event Admin service.
Generally it seems a good idea as it would allow non-DM bundles to interfere with the DM installation.
However, I'm wondering if DM's hook concept fully fits the OSGi event concept. DM hooks are triggered at a high-rate, e.g. fetching a Person's full composite structure triggers the POST_FETCH_TOPIC hook about 20 times. OSGi events on the other hand are probably meant to be rather "high-level" events not fired this rapidly. I'm wondering about the overhead of OSGi events in contrast to native listener-method calls. Probably some profiling would help.
What do you think?

BTW: the DM core already makes use of Event Admin service to fire PLUGIN_READY events to handle plugin dependencies.

comment:4 Changed 8 years ago by dgf

i studied the source a little bit http://svn.apache.org/viewvc/felix/trunk/eventadmin/impl/ and in my opinion, we should give it a try

in preparation we can implement a test szenario and messure the event communication time, a blackbox test over REST should be enough

comment:5 Changed 8 years ago by jri

After discussing it dgf and jri decided the following:
The listener-observer pattern will be realized via native Java classes. Each one of the current hook methods will be replaced by a corresponding listener class. The OSGi Event Admin service will not be involved.
This was decided because of the "low-level" nature of the DM core events. An external application (not using the DM framework) would not be interested in these low-level events. A DM application (coded to the DM framework) on the other hand would use the very listener approach to participate in the DM control flow (and has no need for the OSGi Event Admin service).

The OSGi Event Admin service would be of interest when it comes to loose coupling of DM- and non-DM applications that run in the same OSGi container. Supporting this future scenario would involve the design of higher-level DM events.

comment:6 Changed 8 years ago by Jörg Richter

Core: redesign server-side hook mechanism (#261).

Plugins implement listener interfaces instead of overriding hook methods.

IMPORTANT: developers must adapt their plugins.

The required changes are quite minor. For each used hook a dedicated listener interface must be imported and implemented. The existing hook method can still act as the listener implementation. Only the method name changes slightly (the "Hook" wording is obsolete). The registration of the listeners is performed automatically by the framework by reflecting the implemented interfaces.

See the postCreateHook() of the Workspaces plugin as an example:

Old code:

    public class WorkspacesPlugin extends Plugin implements WorkspacesService {

        @Override
        public void postCreateHook(Topic topic, ClientState clientState, Directives directives) {
            ...
        }
    }

New code:

    import de.deepamehta.core.service.listeners.PostCreateTopicListener;

    public class WorkspacesPlugin extends Plugin implements WorkspacesService, PostCreateTopicListener {

        @Override
        public void postCreateTopic(Topic topic, ClientState clientState, Directives directives) {
            ...
        }
    }

The core service API has 2 new methods:

    List<Object> fireEvent(CoreEvent event, Object... params);
    void addListener(CoreEvent event, Listener listener);

These are usually not to be called by the plugin developer.

For the moment only the postCreateHook() is transformed.
The other hooks will follow.

See ticket 261.

Last edited 8 years ago by jri (previous) (diff)

comment:7 Changed 8 years ago by Jörg Richter

Core: transform 7 more hooks to listeners (#261).

Old Hook Method           New Listener Method        Listener Interface
----------------------------------------------------------------------------------
preCreateHook             -> preCreateTopic          PreCreateTopicListener

preUpdateHook             -> preUpdateTopic          PreUpdateTopicListener
postUpdateHook            -> postUpdateTopic         PostUpdateTopicListener

preDeleteAssociationHook  -> preDeleteAssociation    PreDeleteAssociationListener
postDeleteAssociationHook -> postDeleteAssociation   PostDeleteAssociationListener

preSendTopicHook          -> preSendTopic            PreSendTopicListener
preSendTopicTypeHook      -> preSendTopicType        PreSendTopicTypeListener
----------------------------------------------------------------------------------

2 hooks from DM3 are removed:

    providePropertiesHook(Topic topic)
    providePropertiesHook(Association assoc)

7 more hooks are pending.

See ticket 261.

Last edited 8 years ago by jri (previous) (diff)

comment:8 Changed 8 years ago by dgf

Please add a short class documentation to each listener, so the package javadoc can be linked in the documentation.

Appending Listener to the interfaces is just a matter of taste, but it looks odd for me in a separated listners package.

comment:9 Changed 8 years ago by Jörg Richter

Core: internal events (#261).

A plugin can send an event to itself.
This is needed for the events (hook formerly) that are bound to a particular plugin (instead of fired globally):

  • postInstallPluginHook
  • modifyTopicTypeHook
  • serviceArrived
  • serviceGone

Transformed 1 more hook into a listener:

Old Hook Method           New Listener Method        Listener Interface
----------------------------------------------------------------------------------
postInstallPluginHook     -> postInstallPlugin       PostInstallPluginListener
----------------------------------------------------------------------------------

Package renamed to ...service.listener (instead ...service.listeners)

6 more hooks are pending.

See ticket 261.

Last edited 8 years ago by jri (previous) (diff)

comment:10 Changed 8 years ago by Jörg Richter

Core: all hooks transformed into listeners (#261).

Transformed 5 hooks:

Old Hook Method New Listener Method Listener Interface


postRetypeAssociationHook -> postRetypeAssociation PostRetypeAssociationListener?
allPluginsReadyHook -> allPluginsReady AllPluginsReadyListener?
modifyTopicTypeHook -> introduceTopicType IntroduceTopicTypeListener?
serviceArrived -> serviceArrived ServiceArrivedListener?
serviceGone -> serviceGone ServiceGoneListener?


Removed 1 hook:

executeCommandHook()

Plugin hot deployment doesn't work yet.

Removed 1 core service call:

executeCommand()

The "Commands" concept is obsolete and will be dropped.

Note: the Commands related code is not yet dropped as it still provides a working file uplaod implementation. See:

de.deepamehta.plugins.webservice.WebservicePlugin#executeCommand?()
de.deepamehta.plugins.webservice.provider.CommandParamsProvider?
de.deepamehta.core.util.UploadedFile?

See ticket 261.

Version 0, edited 8 years ago by Jörg Richter (next)

comment:11 Changed 8 years ago by Jörg Richter

Core: fix closing service trackers (#261).

We close the service trackers in reverse creation order.
Consider this case: when a consumed plugin service goes away the core service is still needed to deliver the SERVICE_GONE event.

Plugin redeployment still doesn't work properly.

See ticket 261.

comment:12 Changed 8 years ago by Jörg Richter

Core: unregister event listeners (#261).

When a plugin is stopped it unregisters its event listeners.

Plugin hot redeployment works.

Technically the listeners are fully functional.
The old hook concept and code is now obsolete.

Removal of unused code and code cleanup is pending.

See ticket 261.

comment:13 Changed 8 years ago by Jörg Richter

comment:14 Changed 8 years ago by jri

There are still exceptions when the platform shuts down.
I'll fix them very soon.

comment:15 Changed 8 years ago by Jörg Richter

Core: fix plugin and service tracking (#261).

Plugin redeployment and container shutdown works properly.

Two groups of services are distinguished: a) the 3 "core services" (dms, web publishing, and event admin), and b) the "plugin services" as provided by plugins.
Once the dms is removed from a plugin the trackers of the consumed plugin services are closed. This is necessary because the dms is still needed to deliver the respective SERVICE_GONE events. Once the dms arrives the trackers are opened.

Simplification of plugin dependency tracking: plugin concept of being "ready" is now equivalent with being "registered" (at the core). No separate semantic anymore. No double bookkeeping.

See ticket 261.

comment:16 Changed 8 years ago by Jörg Richter

Core: rename some events (#261).

3 core events are renamed:

SERVICE_ARRIVED -> PLUGIN_SERVICE_ARRIVED
SERVICE_GONE -> PLUGIN_SERVICE_GONE
ALL_PLUGINS_READY -> ALL_PLUGINS_ACTIVE

The listener interfaces and and handler methods are renamed accordingly.

1 internal OSGi event is renamed:

PLUGIN_READY -> PLUGIN_ACTIVATED

See ticket 261.

comment:17 Changed 8 years ago by Jörg Richter

Core: redesign server-side hook mechanism (#261).

Plugins implement listener interfaces instead of overriding hook methods.

IMPORTANT: developers must adapt their plugins.

The required changes are quite minor. For each used hook a dedicated listener interface must be imported and implemented. The existing hook method can still act as the listener implementation. Only the method name changes slightly (the "Hook" wording is obsolete). The registration of the listeners is performed automatically by the framework by reflecting the implemented interfaces.

See the postCreateHook() of the Workspaces plugin as an example:

Old code:

public class WorkspacesPlugin? extends Plugin implements WorkspacesService? {

@Override
public void postCreateHook(Topic topic, ClientState? clientState, Directives directives) {

...

}

}

New code:

import de.deepamehta.core.service.listeners.PostCreateTopicListener?;

public class WorkspacesPlugin? extends Plugin implements WorkspacesService?, PostCreateTopicListener? {

@Override
public void postCreateTopic(Topic topic, ClientState? clientState, Directives directives) {

...

}

}

The core service API has 2 new methods:

List<Object> fireEvent(CoreEvent? event, Object... params);
void addListener(CoreEvent? event, Listener listener);

These are usually not to be called by the plugin developer.

For the moment only the postCreateHook() is transformed.
The other hooks will follow.

See ticket 261.

comment:18 Changed 8 years ago by Jörg Richter

Core: transform 7 more hooks to listeners (#261).

Old Hook Method New Listener Method Listener Interface


preCreateHook -> preCreateTopic PreCreateTopicListener?

preUpdateHook -> preUpdateTopic PreUpdateTopicListener?
postUpdateHook -> postUpdateTopic PostUpdateTopicListener?

preDeleteAssociationHook -> preDeleteAssociation PreDeleteAssociationListener?
postDeleteAssociationHook -> postDeleteAssociation PostDeleteAssociationListener?

preSendTopicHook -> preSendTopic PreSendTopicListener?
preSendTopicTypeHook -> preSendTopicType PreSendTopicTypeListener?


2 hooks from DM3 are removed:

providePropertiesHook(Topic topic)
providePropertiesHook(Association assoc)

7 more hooks are pending.

See ticket 261.

comment:19 Changed 8 years ago by Jörg Richter

Core: internal events (#261).

A plugin can send an event to itself.
This is needed for the events (hook formerly) that are bound to a particular plugin (instead of fired globally):

  • postInstallPluginHook
  • modifyTopicTypeHook
  • serviceArrived
  • serviceGone

Transformed 1 more hook into a listener:

Old Hook Method New Listener Method Listener Interface


postInstallPluginHook -> postInstallPlugin PostInstallPluginListener?


Package renamed to ...service.listener (instead ...service.listeners)

6 more hooks are pending.

See ticket 261.

comment:20 Changed 8 years ago by Jörg Richter

Core: all hooks transformed into listeners (#261).

Transformed 5 hooks:

Old Hook Method New Listener Method Listener Interface


postRetypeAssociationHook -> postRetypeAssociation PostRetypeAssociationListener?
allPluginsReadyHook -> allPluginsReady AllPluginsReadyListener?
modifyTopicTypeHook -> introduceTopicType IntroduceTopicTypeListener?
serviceArrived -> serviceArrived ServiceArrivedListener?
serviceGone -> serviceGone ServiceGoneListener?


Removed 1 hook:

executeCommandHook()

Plugin hot deployment doesn't work yet.

Removed 1 core service call:

executeCommand()

The "Commands" concept is obsolete and will be dropped.

Note: the Commands related code is not yet dropped as it still provides a working file uplaod implementation. See:

de.deepamehta.plugins.webservice.WebservicePlugin#executeCommand?()
de.deepamehta.plugins.webservice.provider.CommandParamsProvider?
de.deepamehta.core.util.UploadedFile?

See ticket 261.

comment:21 Changed 8 years ago by Jörg Richter

Core: fix closing service trackers (#261).

We close the service trackers in reverse creation order.
Consider this case: when a consumed plugin service goes away the core service is still needed to deliver the SERVICE_GONE event.

Plugin redeployment still doesn't work properly.

See ticket 261.

comment:22 Changed 8 years ago by Jörg Richter

Core: unregister event listeners (#261).

When a plugin is stopped it unregisters its event listeners.

Plugin hot redeployment works.

Technically the listeners are fully functional.
The old hook concept and code is now obsolete.

Removal of unused code and code cleanup is pending.

See ticket 261.

comment:23 Changed 8 years ago by Jörg Richter

comment:24 Changed 8 years ago by Jörg Richter

Core: fix plugin and service tracking (#261).

Plugin redeployment and container shutdown works properly.

Two groups of services are distinguished: a) the 3 "core services" (dms, web publishing, and event admin), and b) the "plugin services" as provided by plugins.
Once the dms is removed from a plugin the trackers of the consumed plugin services are closed. This is necessary because the dms is still needed to deliver the respective SERVICE_GONE events. Once the dms arrives the trackers are opened.

Simplification of plugin dependency tracking: plugin concept of being "ready" is now equivalent with being "registered" (at the core). No separate semantic anymore. No double bookkeeping.

See ticket 261.

comment:25 Changed 8 years ago by Jörg Richter

Core: rename some events (#261).

3 core events are renamed:

SERVICE_ARRIVED -> PLUGIN_SERVICE_ARRIVED
SERVICE_GONE -> PLUGIN_SERVICE_GONE
ALL_PLUGINS_READY -> ALL_PLUGINS_ACTIVE

The listener interfaces and and handler methods are renamed accordingly.

1 internal OSGi event is renamed:

PLUGIN_READY -> PLUGIN_ACTIVATED

See ticket 261.

comment:26 Changed 8 years ago by jri

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

comment:27 Changed 8 years ago by Jörg Richter

Core: redesign server-side hook mechanism (#261).

Plugins implement listener interfaces instead of overriding hook methods.

IMPORTANT: developers must adapt their plugins.

The required changes are quite minor. For each used hook a dedicated listener interface must be imported and implemented. The existing hook method can still act as the listener implementation. Only the method name changes slightly (the "Hook" wording is obsolete). The registration of the listeners is performed automatically by the framework by reflecting the implemented interfaces.

See the postCreateHook() of the Workspaces plugin as an example:

Old code:

public class WorkspacesPlugin? extends Plugin implements WorkspacesService? {

@Override
public void postCreateHook(Topic topic, ClientState? clientState, Directives directives) {

...

}

}

New code:

import de.deepamehta.core.service.listeners.PostCreateTopicListener?;

public class WorkspacesPlugin? extends Plugin implements WorkspacesService?, PostCreateTopicListener? {

@Override
public void postCreateTopic(Topic topic, ClientState? clientState, Directives directives) {

...

}

}

The core service API has 2 new methods:

List<Object> fireEvent(CoreEvent? event, Object... params);
void addListener(CoreEvent? event, Listener listener);

These are usually not to be called by the plugin developer.

For the moment only the postCreateHook() is transformed.
The other hooks will follow.

See ticket 261.

comment:28 Changed 8 years ago by Jörg Richter

Core: transform 7 more hooks to listeners (#261).

Old Hook Method New Listener Method Listener Interface


preCreateHook -> preCreateTopic PreCreateTopicListener?

preUpdateHook -> preUpdateTopic PreUpdateTopicListener?
postUpdateHook -> postUpdateTopic PostUpdateTopicListener?

preDeleteAssociationHook -> preDeleteAssociation PreDeleteAssociationListener?
postDeleteAssociationHook -> postDeleteAssociation PostDeleteAssociationListener?

preSendTopicHook -> preSendTopic PreSendTopicListener?
preSendTopicTypeHook -> preSendTopicType PreSendTopicTypeListener?


2 hooks from DM3 are removed:

providePropertiesHook(Topic topic)
providePropertiesHook(Association assoc)

7 more hooks are pending.

See ticket 261.

comment:29 Changed 8 years ago by Jörg Richter

Core: internal events (#261).

A plugin can send an event to itself.
This is needed for the events (hook formerly) that are bound to a particular plugin (instead of fired globally):

  • postInstallPluginHook
  • modifyTopicTypeHook
  • serviceArrived
  • serviceGone

Transformed 1 more hook into a listener:

Old Hook Method New Listener Method Listener Interface


postInstallPluginHook -> postInstallPlugin PostInstallPluginListener?


Package renamed to ...service.listener (instead ...service.listeners)

6 more hooks are pending.

See ticket 261.

comment:30 Changed 8 years ago by Jörg Richter

Core: all hooks transformed into listeners (#261).

Transformed 5 hooks:

Old Hook Method New Listener Method Listener Interface


postRetypeAssociationHook -> postRetypeAssociation PostRetypeAssociationListener?
allPluginsReadyHook -> allPluginsReady AllPluginsReadyListener?
modifyTopicTypeHook -> introduceTopicType IntroduceTopicTypeListener?
serviceArrived -> serviceArrived ServiceArrivedListener?
serviceGone -> serviceGone ServiceGoneListener?


Removed 1 hook:

executeCommandHook()

Plugin hot deployment doesn't work yet.

Removed 1 core service call:

executeCommand()

The "Commands" concept is obsolete and will be dropped.

Note: the Commands related code is not yet dropped as it still provides a working file uplaod implementation. See:

de.deepamehta.plugins.webservice.WebservicePlugin#executeCommand?()
de.deepamehta.plugins.webservice.provider.CommandParamsProvider?
de.deepamehta.core.util.UploadedFile?

See ticket 261.

comment:31 Changed 8 years ago by Jörg Richter

Core: fix closing service trackers (#261).

We close the service trackers in reverse creation order.
Consider this case: when a consumed plugin service goes away the core service is still needed to deliver the SERVICE_GONE event.

Plugin redeployment still doesn't work properly.

See ticket 261.

comment:32 Changed 8 years ago by Jörg Richter

Core: unregister event listeners (#261).

When a plugin is stopped it unregisters its event listeners.

Plugin hot redeployment works.

Technically the listeners are fully functional.
The old hook concept and code is now obsolete.

Removal of unused code and code cleanup is pending.

See ticket 261.

comment:33 Changed 8 years ago by Jörg Richter

comment:34 Changed 8 years ago by Jörg Richter

Core: fix plugin and service tracking (#261).

Plugin redeployment and container shutdown works properly.

Two groups of services are distinguished: a) the 3 "core services" (dms, web publishing, and event admin), and b) the "plugin services" as provided by plugins.
Once the dms is removed from a plugin the trackers of the consumed plugin services are closed. This is necessary because the dms is still needed to deliver the respective SERVICE_GONE events. Once the dms arrives the trackers are opened.

Simplification of plugin dependency tracking: plugin concept of being "ready" is now equivalent with being "registered" (at the core). No separate semantic anymore. No double bookkeeping.

See ticket 261.

comment:35 Changed 8 years ago by Jörg Richter

Core: rename some events (#261).

3 core events are renamed:

SERVICE_ARRIVED -> PLUGIN_SERVICE_ARRIVED
SERVICE_GONE -> PLUGIN_SERVICE_GONE
ALL_PLUGINS_READY -> ALL_PLUGINS_ACTIVE

The listener interfaces and and handler methods are renamed accordingly.

1 internal OSGi event is renamed:

PLUGIN_READY -> PLUGIN_ACTIVATED

See ticket 261.

Note: See TracTickets for help on using tickets.