Core: unregister plugin OSGi service
Core: use a Jersey servlet singleton (#272).

We hope this will solve the oddities with module hot deployment.

The singleton is encapsulated in a new OSGi "WebPublishingService" as provided by the DM core.
The plugins register their resources through this service.

Not fully functional.

See ticket 272.
Core: adapt plugin REST services (#272).

A plugin's provider classes are picked up automatically.
A plugin must no longer provide an Application class.

The Webclient's start URL has changed:
    http://localhost:8080/de.deepamehta.webclient/
The root namespace is already occupied by the Jersey root application.

IMPORTANT: To adapt to the new Webclient base URL you must reset your database.

Not fully functional.

See ticket 272.
Core: WebPublishing service basically works (#272)

Plugins add their REST resources via the WebPublishing service.
Removing resources is pending.

Plugin's "Application" classes are dropped.

See ticket 272.
Core: removing REST resources dynamically (#272).

Reloading a module (via hot deployment) that provides REST resources works properly now. Other modules are not affected.
So, the main problem seems to be solved now.

Secondary issues remain (e.g. reloading the core, or shutdown) and will be addressed shortly.

Hi dgf! Your intuition with the multiple Jersey servlet instances as the culprit seems to prove correct.
Thank you!

See ticket 272.
Core: dynamic un/register Jersey servlet (#272).

Once all REST resources are removed the Jersey servlet is unregistered.
As soon as resources are added the Jersey servlet is registered again.
So, actions like reloading the DM core or shutdown the platform perform properly.

Note: there is still an issue with reloading the Webclient module but this is not REST related and needs to be addressed in another ticket.

See ticket 272.
Core: abolish resource config properties (#272).

2 properties are no longer required to be set in plugin.properties:
    "restResourcesNamespace" -- now set in the plugin class's @Path anotation
    "webResourcesNamespace" -- not configurable anymore

See ticket 272.
Core: plugins doesn't consume HttpService (#272).

Instead they are register their web resources through the WebPublishingService.

WebPublishingService provides 2 new methods:
    WebResources addWebResources(Bundle bundle, String uriNamespace)
    void removeWebResources(WebResources webResources)

All web publishing features are encapsulated in the WebPublishingService.

See ticket 272.
Core fix: don't initialize a plugin twice (#274).

This solves "Servlet with alias already registered".
Redeploying e.g. the Webclient works properly.

Plugin code is cleaned up.

See ticket 274.
Webclient: plugin classes not in global space #275

IMPORTANT: plugin developers must adapt their plugin classes.
The change pattern is straight forward. Just replace...

    function my_plugin() {
        ...
    }

by

    dm4c.add_plugin("my_plugin", function() {
        ...
    })

See ticket 275.
Webclient: fix copy-paste typo (#275).

See ticket 275.
Webclient: auto pick up plugin classes (#276).

The "clientSidePluginFile" plugin config property is obsolete.

Wording in API: "Plugin ID" is now called "Plugin URI".

IMPORTANT: plugin developers must rename the plugin's main file to "plugin.js"
Name and location is fixed:

    /web/script/plugin.js

Hint: if you use Maven resource filtering for your plugin main file don't forget to adapt the POM.

See ticket 276.
Webclient: plugin namespace convention (#275).

IMPORTANT for plugin developers:
For proper namespacing there is the convention to use the plugin's URI when registering via dm4c.add_plugin():

    dm4c.add_plugin("de.deepamehta.topicmaps", function() {
        ...
    })

Technically this string is arbitrary. It's just the identifier by which you can access the plugin later on, usually to call its public API methods:

    dm4c.get_plugin("de.deepamehta.topicmaps").get_topicmap()

However, the string must be globally unique, so it is most natural to use the plugin's URI. Actually the plugin URI is the plugin bundle's symbolic name, which is calculated from the plugin project's group ID and artifact ID.

See ticket 275.

Furthermore in Webclient: exceptions that occur while loading and executing a script via dm4c.load_script() are rethrown to let them appear in the browser's error console.
Webclient: rename dm4c.register_listener()

... to dm4c.add_listener().

This wording is more friendly/convenient/clear/popular.

IMPORTANT: developers must adapt their plugins accordingly.
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.
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.
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.
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.
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.
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.