You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2019/09/30 10:36:01 UTC

[isis] branch v2 updated: ISIS-2158: moves Server-Sent-Events feature -> 'extensions/sse'

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch v2
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/v2 by this push:
     new af286e2  ISIS-2158: moves Server-Sent-Events feature -> 'extensions/sse'
af286e2 is described below

commit af286e2c80ee12aa88e1fc772bd57586aba31f2e
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Sep 30 12:35:51 2019 +0200

    ISIS-2158: moves Server-Sent-Events feature -> 'extensions/sse'
    
    - SSE (API) is experimental, so removed from 'core'
---
 antora/components/toc/modules/ROOT/nav.adoc        |  1 +
 .../components/toc/modules/ROOT/pages/about.adoc   |  1 +
 .../toc/modules/ROOT/pages/components.adoc         |  1 +
 .../apache/isis/applib/annotation/Property.java    | 10 ---
 .../property/PropertyAnnotationFacetFactory.java   | 17 +---
 .../progmodel/ProgrammingModelPlugin.java          |  2 +-
 .../dflt/ProgrammingModelFacetsJava8.java          |  4 +-
 .../org/apache/isis/webapp/IsisBootWebApp.java     |  2 -
 .../components/scalars/markup/MarkupComponent.java | 16 +---
 .../scalars/markup/MarkupPanelFactories.java       | 20 +----
 .../wicket/ui/errors/ExceptionStackTracePanel.java |  2 +-
 examples/apps/demo/pom.xml                         | 12 +++
 .../domainapp/application/DemoAppManifest.java     |  2 +
 .../dom/actions/async/AsyncActionDemo.java         | 16 ++--
 .../java/domainapp/dom/actions/async/DemoTask.java | 13 ++--
 .../extensions/asciidoc/AsciiDocComponent.java     |  2 +-
 extensions/incubator/pom.xml                       |  6 ++
 ...rtingMethodPlugin.java => IncubatorPlugin.java} |  7 +-
 ...isis.metamodel.progmodel.ProgrammingModelPlugin |  2 +-
 .../extensions/markdown/MarkdownComponent.java     |  2 +-
 extensions/pom.xml                                 | 10 ++-
 extensions/sse/_adoc/antora.yml                    |  6 ++
 extensions/sse/_adoc/examples.csv                  |  1 +
 extensions/sse/_adoc/modules/ROOT/_attributes.adoc |  6 ++
 .../_adoc/modules/ROOT/assets/attachments/.gitkeep |  0
 .../sse/_adoc/modules/ROOT/assets/images/.gitkeep  |  0
 .../sse/_adoc/modules/ROOT/examples/.gitkeep       |  0
 extensions/sse/_adoc/modules/ROOT/nav.adoc         |  2 +
 .../sse/_adoc/modules/ROOT/pages/_attributes.adoc  |  4 +
 .../modules/ROOT/pages/_partials/_attributes.adoc  |  4 +
 extensions/sse/_adoc/modules/ROOT/pages/about.adoc |  6 ++
 extensions/sse/_adoc/sync_examples.sh              | 39 ++++++++++
 extensions/{incubator => sse}/pom.xml              | 26 ++++---
 .../apache/isis/extensions/sse/IsisBootSse.java    | 20 ++---
 .../org/apache/isis/extensions/sse/SsePlugin.java} | 11 ++-
 .../isis/extensions/sse/api/ServerSentEvents.java  | 34 ++++----
 .../apache/isis/extensions/sse/api/SseChannel.java |  8 +-
 .../apache/isis/extensions/sse/api/SseService.java |  8 +-
 .../apache/isis/extensions/sse/api/SseSource.java  | 22 +++---
 .../isis/extensions/sse/facets}/ObserveFacet.java  | 15 +++-
 .../sse/facets}/ObserveFacetAbstract.java          | 10 +--
 .../sse/facets/ObserveFacetForSseAnnotation.java   | 22 +++---
 .../sse/facets/SseAnnotationFacetFactory.java      | 63 +++++++++++++++
 .../extensions/sse/markup/ListeningMarkup.java     | 28 +++++++
 .../sse/markup/ListeningMarkupComponent.java       | 55 +++----------
 .../markup/ListeningMarkupComponent_observing.java |  8 +-
 .../ListeningMarkupPanelFactoriesForWicket.java    | 90 ++++++++++++++++++++++
 .../sse/markup/js}/ObservingComponent.js           |  0
 .../sse/services}/EventStreamServiceDefault.java   | 32 ++++----
 .../sse/webapp}/ServerSentEventsServlet.java       | 17 ++--
 .../sse/webapp}/WebModuleServerSentEvents.java     |  2 +-
 ...isis.metamodel.progmodel.ProgrammingModelPlugin |  1 +
 ...g.apache.isis.viewer.wicket.ui.ComponentFactory |  2 +
 53 files changed, 444 insertions(+), 246 deletions(-)

diff --git a/antora/components/toc/modules/ROOT/nav.adoc b/antora/components/toc/modules/ROOT/nav.adoc
index da08eae..ba452c3 100644
--- a/antora/components/toc/modules/ROOT/nav.adoc
+++ b/antora/components/toc/modules/ROOT/nav.adoc
@@ -36,6 +36,7 @@
 ** xref:ext-viewer-wicket-excel:ROOT:about.adoc[Excel Download (extension)]
 ** xref:ext-asciidoc:ROOT:about.adoc[Asciidoc value type (extension)]
 ** xref:ext-markdown:ROOT:about.adoc[Markdown value type (extension)]
+** xref:ext-sse:ROOT:about.adoc[Server Sent Events (extension)]
 
 * Restful Objects Viewer
 
diff --git a/antora/components/toc/modules/ROOT/pages/about.adoc b/antora/components/toc/modules/ROOT/pages/about.adoc
index 2f32389..24c28fd 100644
--- a/antora/components/toc/modules/ROOT/pages/about.adoc
+++ b/antora/components/toc/modules/ROOT/pages/about.adoc
@@ -55,6 +55,7 @@ _Extensions:_
 * xref:ext-viewer-wicket-excel:ROOT:about.adoc[Excel Download]
 * xref:ext-asciidoc:ROOT:about.adoc[Asciidoc value type ]
 * xref:ext-markdown:ROOT:about.adoc[Markdown value type]
+* xref:ext-sse:ROOT:about.adoc[Server Sent Events]
 * xref:ext-incubator:ROOT:about.adoc[Experimental framework extensions]
 
 | *xref:toc:ROOT:components.adoc#restful-objects-viewer[Restful Objects Viewer]*
diff --git a/antora/components/toc/modules/ROOT/pages/components.adoc b/antora/components/toc/modules/ROOT/pages/components.adoc
index 73fa1b3..af39a27 100644
--- a/antora/components/toc/modules/ROOT/pages/components.adoc
+++ b/antora/components/toc/modules/ROOT/pages/components.adoc
@@ -15,6 +15,7 @@ _Extensions:_
 * xref:ext-viewer-wicket-excel:ROOT:about.adoc[Excel Download]
 * xref:ext-asciidoc:ROOT:about.adoc[Asciidoc value type ]
 * xref:ext-markdown:ROOT:about.adoc[Markdown value type]
+* xref:ext-sse:ROOT:about.adoc[Server Sent Events]
 * xref:ext-incubator:ROOT:about.adoc[Experimental framework extensions]
 
 == Restful Objects Viewer
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java
index e66d2b2..1ca48a5 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java
@@ -30,7 +30,6 @@ import javax.jdo.annotations.NotPersistent;
 import org.apache.isis.applib.conmap.ContentMappingServiceForCommandDto;
 import org.apache.isis.applib.conmap.ContentMappingServiceForCommandsDto;
 import org.apache.isis.applib.events.domain.PropertyDomainEvent;
-import org.apache.isis.applib.events.sse.EventStreamSource;
 import org.apache.isis.applib.services.command.CommandDtoProcessor;
 import org.apache.isis.applib.services.command.CommandWithDto;
 import org.apache.isis.applib.services.command.spi.CommandService;
@@ -256,13 +255,4 @@ public @interface Property {
     String fileAccept() default "";
 
 
-
-
-
-    /**
-     * TODO
-     *
-     */
-    Class<? extends EventStreamSource> observe() default EventStreamSource.Noop.class;
-
 }
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
index a449c58..ef38580 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
@@ -43,7 +43,6 @@ import org.apache.isis.metamodel.facets.object.domainobject.domainevents.Propert
 import org.apache.isis.metamodel.facets.objectvalue.fileaccept.FileAcceptFacet;
 import org.apache.isis.metamodel.facets.objectvalue.mandatory.MandatoryFacet;
 import org.apache.isis.metamodel.facets.objectvalue.maxlen.MaxLengthFacet;
-import org.apache.isis.metamodel.facets.objectvalue.observe.ObserveFacet;
 import org.apache.isis.metamodel.facets.objectvalue.regex.RegExFacet;
 import org.apache.isis.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
 import org.apache.isis.metamodel.facets.propcoll.notpersisted.NotPersistedFacet;
@@ -65,7 +64,6 @@ import org.apache.isis.metamodel.facets.properties.property.modify.PropertySette
 import org.apache.isis.metamodel.facets.properties.property.modify.PropertySetterFacetForDomainEventFromPropertyAnnotation;
 import org.apache.isis.metamodel.facets.properties.property.mustsatisfy.MustSatisfySpecificationFacetForPropertyAnnotation;
 import org.apache.isis.metamodel.facets.properties.property.notpersisted.NotPersistedFacetForPropertyAnnotation;
-import org.apache.isis.metamodel.facets.properties.property.observe.ObserveFacetForPropertyAnnotation;
 import org.apache.isis.metamodel.facets.properties.property.publishing.PublishedPropertyFacetForPropertyAnnotation;
 import org.apache.isis.metamodel.facets.properties.property.regex.RegExFacetForPatternAnnotationOnProperty;
 import org.apache.isis.metamodel.facets.properties.property.regex.RegExFacetForPropertyAnnotation;
@@ -102,7 +100,6 @@ implements MetaModelValidatorRefiner {
         processOptional(processMethodContext);
         processRegEx(processMethodContext);
         processFileAccept(processMethodContext);
-        processObserve(processMethodContext);
     }
 
 
@@ -251,7 +248,7 @@ implements MetaModelValidatorRefiner {
 
     void processProjecting(final ProcessMethodContext processMethodContext) {
 
-        final Class<?> cls = processMethodContext.getCls();
+        //final Class<?> cls = processMethodContext.getCls();
         final Method method = processMethodContext.getMethod();
         final Property property = Annotations.getAnnotation(method, Property.class);
         final FacetedMethod facetHolder = processMethodContext.getFacetHolder();
@@ -376,18 +373,6 @@ implements MetaModelValidatorRefiner {
         FacetUtil.addFacet(facet);
     }
 
-    void processObserve(final ProcessMethodContext processMethodContext) {
-        final Method method = processMethodContext.getMethod();
-        final FacetHolder holder = processMethodContext.getFacetHolder();
-
-        // else search for @Property(observe=...)
-        final List<Property> properties = Annotations.getAnnotations(method, Property.class);
-        ObserveFacet facet = ObserveFacetForPropertyAnnotation.create(properties, holder);
-
-        FacetUtil.addFacet(facet);
-    }
-
-
     // //////////////////////////////////////
 
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/progmodel/ProgrammingModelPlugin.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/progmodel/ProgrammingModelPlugin.java
index e95f08f..c73b206 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/progmodel/ProgrammingModelPlugin.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/progmodel/ProgrammingModelPlugin.java
@@ -44,7 +44,7 @@ public interface ProgrammingModelPlugin {
         /**
          * registers the provided factory after all built-in factories
          */
-        VALIDATION,
+        AFTER_BUILT_IN,
         ;
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/progmodels/dflt/ProgrammingModelFacetsJava8.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/progmodels/dflt/ProgrammingModelFacetsJava8.java
index 432bd4f..2ec213a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/progmodels/dflt/ProgrammingModelFacetsJava8.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/progmodels/dflt/ProgrammingModelFacetsJava8.java
@@ -411,8 +411,8 @@ public final class ProgrammingModelFacetsJava8 extends ProgrammingModelAbstract
 
         addFactory(new ViewModelSemanticCheckingFacetFactory());
         
-        // plugin value factories
-        factoriesFromPlugins.getFactories(FacetFactoryCategory.VALIDATION).forEach(this::addFactory);
+        // plugin factories
+        factoriesFromPlugins.getFactories(FacetFactoryCategory.AFTER_BUILT_IN).forEach(this::addFactory);
 
     }
 
diff --git a/core/runtime-web/src/main/java/org/apache/isis/webapp/IsisBootWebApp.java b/core/runtime-web/src/main/java/org/apache/isis/webapp/IsisBootWebApp.java
index 0f6c214..7688446 100644
--- a/core/runtime-web/src/main/java/org/apache/isis/webapp/IsisBootWebApp.java
+++ b/core/runtime-web/src/main/java/org/apache/isis/webapp/IsisBootWebApp.java
@@ -25,7 +25,6 @@ import org.apache.isis.webapp.modules.h2console.H2ManagerMenu;
 import org.apache.isis.webapp.modules.h2console.WebModuleH2Console;
 import org.apache.isis.webapp.modules.logonlog.WebModuleLogOnExceptionLogger;
 import org.apache.isis.webapp.modules.resources.WebModuleStaticResources;
-import org.apache.isis.webapp.modules.sse.WebModuleServerSentEvents;
 
 @Configuration
 @Import({
@@ -35,7 +34,6 @@ import org.apache.isis.webapp.modules.sse.WebModuleServerSentEvents;
     // default modules
     WebModuleLogOnExceptionLogger.class,
     WebModuleStaticResources.class,
-    WebModuleServerSentEvents.class,
 
     // h2 console
     WebModuleH2Console.class,
diff --git a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java
index 18c55d5..e435c07 100644
--- a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java
+++ b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java
@@ -25,7 +25,6 @@ import org.apache.wicket.markup.html.WebComponent;
 import org.apache.wicket.markup.parser.XmlTag.TagType;
 import org.apache.wicket.model.IModel;
 
-import org.apache.isis.applib.value.LocalResourcePath;
 import org.apache.isis.applib.value.Markup;
 import org.apache.isis.metamodel.adapter.ObjectAdapter;
 
@@ -35,25 +34,14 @@ public class MarkupComponent extends WebComponent {
 
     private static final long serialVersionUID = 1L;
 
-    private final LocalResourcePath observing;
-
-    public MarkupComponent(final String id, IModel<?> model, LocalResourcePath observing){
+    public MarkupComponent(final String id, IModel<?> model){
         super(id, model);
-        this.observing = observing;
     }
 
     @Override
     public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag){
         val htmlContent = extractHtmlOrElse(getDefaultModelObject(), "" /*fallback*/);
-        replaceComponentTagBody(
-                markupStream, 
-                openTag, 
-
-                observing!=null 
-                ? MarkupComponent_observing.decorate(htmlContent, observing)
-                        : htmlContent
-
-                );
+        replaceComponentTagBody(markupStream, openTag, htmlContent);
     }
 
     @Override
diff --git a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupPanelFactories.java b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupPanelFactories.java
index bc1bd1c..88db3ef 100644
--- a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupPanelFactories.java
+++ b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupPanelFactories.java
@@ -22,9 +22,7 @@ package org.apache.isis.viewer.wicket.ui.components.scalars.markup;
 import org.apache.wicket.Component;
 import org.apache.wicket.model.IModel;
 
-import org.apache.isis.applib.value.LocalResourcePath;
 import org.apache.isis.applib.value.Markup;
-import org.apache.isis.metamodel.facets.objectvalue.observe.ObserveFacet;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
 import org.apache.isis.viewer.wicket.model.models.ValueModel;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
@@ -129,25 +127,12 @@ public class MarkupPanelFactories {
         @Override
         protected MarkupComponentFactory getMarkupComponentFactory() {
             return (id, model) -> {
-                val markupComponent = new MarkupComponent(
-                        id, model, getEventStreamResource((ScalarModel)model));
+                val markupComponent = new MarkupComponent(id, model);
                 markupComponent.setEnabled(false);
                 return markupComponent;    
             };
         }
 
-        // -- HELPER
-
-        private LocalResourcePath getEventStreamResource(ScalarModel scalarModel) {
-            final ObserveFacet observeFacet  = scalarModel.getFacet(ObserveFacet.class);
-            if(observeFacet==null) {
-                return null;
-            }
-            final String eventStreamId = observeFacet.getEventStreamType().getName();
-            final LocalResourcePath ssePath = new LocalResourcePath("/sse?eventStream=" + eventStreamId);
-            return ssePath;
-        }
-
     }
 
     // -- CONCRETE COMPONENT FACTORY - STANDALONE
@@ -162,8 +147,7 @@ public class MarkupPanelFactories {
         @Override
         protected MarkupComponentFactory getMarkupComponentFactory() {
             return (id, model) -> {
-                val markupComponent = new MarkupComponent(
-                        id, model, /*observing not supported*/ null);
+                val markupComponent = new MarkupComponent(id, model);
                 return markupComponent;    
             };
         }
diff --git a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.java b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.java
index f7443b6..e5c69d7 100644
--- a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.java
+++ b/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.java
@@ -101,7 +101,7 @@ public class ExceptionStackTracePanel extends Panel {
                 if(ticketMarkup == null) {
                     Components.permanentlyHide(this, ID_TICKET_MARKUP);
                 } else {
-                    add(new MarkupComponent(ID_TICKET_MARKUP, Model.of(ticket.getMarkup()), null /*observing*/));
+                    add(new MarkupComponent(ID_TICKET_MARKUP, Model.of(ticket.getMarkup())));
                 }
 
                 final boolean suppressExceptionDetail =
diff --git a/examples/apps/demo/pom.xml b/examples/apps/demo/pom.xml
index c4509d6..5f9589d 100644
--- a/examples/apps/demo/pom.xml
+++ b/examples/apps/demo/pom.xml
@@ -73,6 +73,18 @@
 		</dependency>
 
 		<!-- ISIS EXTENSIONS -->
+		
+		<dependency>
+			<groupId>org.apache.isis.extensions</groupId>
+			<artifactId>isis-extensions-incubator</artifactId>
+			<version>${isis.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.isis.extensions</groupId>
+			<artifactId>isis-extensions-sse</artifactId>
+			<version>${isis.version}</version>
+		</dependency>
 
 		<dependency>
 			<groupId>org.apache.isis.extensions</groupId>
diff --git a/examples/apps/demo/src/main/java/domainapp/application/DemoAppManifest.java b/examples/apps/demo/src/main/java/domainapp/application/DemoAppManifest.java
index bdca113..b04e97e 100644
--- a/examples/apps/demo/src/main/java/domainapp/application/DemoAppManifest.java
+++ b/examples/apps/demo/src/main/java/domainapp/application/DemoAppManifest.java
@@ -38,6 +38,7 @@ import org.apache.isis.extensions.secman.encryption.jbcrypt.IsisBootSecmanEncryp
 import org.apache.isis.extensions.secman.jdo.IsisBootSecmanPersistenceJdo;
 import org.apache.isis.extensions.secman.model.IsisBootSecmanModel;
 import org.apache.isis.extensions.secman.shiro.IsisBootSecmanRealmShiro;
+import org.apache.isis.extensions.sse.IsisBootSse;
 import org.apache.isis.jdo.IsisBootDataNucleus;
 import org.apache.isis.runtime.spring.IsisBoot;
 import org.apache.isis.security.shiro.IsisBootSecurityShiro;
@@ -60,6 +61,7 @@ import domainapp.utils.LibraryPreloadingService;
     IsisBootSecurityShiro.class,
     IsisBootDataNucleus.class,
     IsisBootWebWicket.class,
+    IsisBootSse.class, // server sent events
 
     // Security Manager Extension (secman)
     IsisBootSecmanModel.class,
diff --git a/examples/apps/demo/src/main/java/domainapp/dom/actions/async/AsyncActionDemo.java b/examples/apps/demo/src/main/java/domainapp/dom/actions/async/AsyncActionDemo.java
index 8ef1a26..9096ef1 100644
--- a/examples/apps/demo/src/main/java/domainapp/dom/actions/async/AsyncActionDemo.java
+++ b/examples/apps/demo/src/main/java/domainapp/dom/actions/async/AsyncActionDemo.java
@@ -32,10 +32,11 @@ import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.Editing;
 import org.apache.isis.applib.annotation.Nature;
 import org.apache.isis.applib.annotation.Property;
-import org.apache.isis.applib.events.sse.EventStreamService;
-import org.apache.isis.applib.events.sse.EventStreamService.ExecutionBehavior;
 import org.apache.isis.applib.util.JaxbAdapters;
-import org.apache.isis.applib.value.Markup;
+import org.apache.isis.extensions.sse.api.SseService;
+import org.apache.isis.extensions.sse.api.SseService.ExecutionBehavior;
+import org.apache.isis.extensions.sse.api.ServerSentEvents;
+import org.apache.isis.extensions.sse.markup.ListeningMarkup;
 
 import domainapp.utils.DemoStub;
 import lombok.Getter;
@@ -49,11 +50,12 @@ import lombok.val;
 public class AsyncActionDemo extends DemoStub {
 
     @XmlTransient
-    @Inject EventStreamService eventStreamService;
+    @Inject SseService eventStreamService;
 
     @XmlElement @XmlJavaTypeAdapter(JaxbAdapters.MarkupAdapter.class)
-    @Property(observe=DemoTask.class)
-    @Getter @Setter Markup progressView;
+    @Property
+    @ServerSentEvents(observe=DemoTask.class) // bind to a SSE channel
+    @Getter @Setter ListeningMarkup progressView;
 
     @Action
     public AsyncActionDemo startSimpleTask() {
@@ -75,7 +77,7 @@ public class AsyncActionDemo extends DemoStub {
 
     @Override
     public void initDefaults() {
-        progressView = new Markup("Please start a task!");
+        progressView = ListeningMarkup.valueOfHtml("Please start a task!");
     }
 
 }
diff --git a/examples/apps/demo/src/main/java/domainapp/dom/actions/async/DemoTask.java b/examples/apps/demo/src/main/java/domainapp/dom/actions/async/DemoTask.java
index f62e50a..1913caa 100644
--- a/examples/apps/demo/src/main/java/domainapp/dom/actions/async/DemoTask.java
+++ b/examples/apps/demo/src/main/java/domainapp/dom/actions/async/DemoTask.java
@@ -23,15 +23,14 @@ import java.util.concurrent.atomic.LongAdder;
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.Editing;
 import org.apache.isis.applib.annotation.Nature;
-import org.apache.isis.applib.events.sse.EventStream;
-import org.apache.isis.applib.events.sse.EventStreamSource;
-import org.apache.isis.applib.value.Markup;
+import org.apache.isis.extensions.sse.api.SseChannel;
+import org.apache.isis.extensions.sse.api.SseSource;
 
 import lombok.RequiredArgsConstructor;
 
 @DomainObject(nature=Nature.INMEMORY_ENTITY, editing=Editing.DISABLED) 
 @RequiredArgsConstructor(staticName="of")
-public class DemoTask implements EventStreamSource {
+public class DemoTask implements SseSource {
 
     public String title() {
         return String.format("DemoTask '%s'", Integer.toHexString(hashCode()));
@@ -42,7 +41,7 @@ public class DemoTask implements EventStreamSource {
 
 
     @Override
-    public void run(EventStream eventStream) {
+    public void run(SseChannel eventStream) {
 
         taskProgress = TaskProgress.of(new LongAdder(), totalSteps);
 
@@ -63,8 +62,8 @@ public class DemoTask implements EventStreamSource {
     }
 
     @Override
-    public Markup getPayload() {
-        return new Markup("" + taskProgress + "<br/>" + taskProgress.toHtmlProgressBar());
+    public String getPayload() {
+        return "" + taskProgress + "<br/>" + taskProgress.toHtmlProgressBar();
     }
 
 
diff --git a/extensions/asciidoc/src/main/java/org/apache/isis/extensions/asciidoc/AsciiDocComponent.java b/extensions/asciidoc/src/main/java/org/apache/isis/extensions/asciidoc/AsciiDocComponent.java
index 92a0558..30787a0 100644
--- a/extensions/asciidoc/src/main/java/org/apache/isis/extensions/asciidoc/AsciiDocComponent.java
+++ b/extensions/asciidoc/src/main/java/org/apache/isis/extensions/asciidoc/AsciiDocComponent.java
@@ -38,7 +38,7 @@ public class AsciiDocComponent extends MarkupComponent {
     private static final long serialVersionUID = 1L;
 
     public AsciiDocComponent(String id, IModel<?> model) {
-        super(id, model, /*observing*/ null);
+        super(id, model);
     }
 
     @Override
diff --git a/extensions/incubator/pom.xml b/extensions/incubator/pom.xml
index 03d3af4..1900d71 100644
--- a/extensions/incubator/pom.xml
+++ b/extensions/incubator/pom.xml
@@ -38,6 +38,12 @@
 		</dependency>
 		
 		<dependency>
+			<groupId>org.apache.isis.core</groupId>
+			<artifactId>isis-runtime-web</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		
+		<dependency>
             <groupId>org.apache.isis.core</groupId>
             <artifactId>isis-viewer-wicket-ui</artifactId>
             <scope>provided</scope>
diff --git a/extensions/incubator/src/main/java/org/apache/isis/extensions/incubator/SupportingMethodPlugin.java b/extensions/incubator/src/main/java/org/apache/isis/extensions/incubator/IncubatorPlugin.java
similarity index 83%
copy from extensions/incubator/src/main/java/org/apache/isis/extensions/incubator/SupportingMethodPlugin.java
copy to extensions/incubator/src/main/java/org/apache/isis/extensions/incubator/IncubatorPlugin.java
index 20cde3b..49bce9f 100644
--- a/extensions/incubator/src/main/java/org/apache/isis/extensions/incubator/SupportingMethodPlugin.java
+++ b/extensions/incubator/src/main/java/org/apache/isis/extensions/incubator/IncubatorPlugin.java
@@ -21,11 +21,14 @@ package org.apache.isis.extensions.incubator;
 import org.apache.isis.metamodel.facets.actions.support.SupportingMethodValidatorRefinerFactory;
 import org.apache.isis.metamodel.progmodel.ProgrammingModelPlugin;
 
-public class SupportingMethodPlugin implements ProgrammingModelPlugin {
+public class IncubatorPlugin implements ProgrammingModelPlugin {
 
     @Override
     public void plugin(FactoryCollector collector) {
-        collector.addFactory(new SupportingMethodValidatorRefinerFactory(), FacetFactoryCategory.VALIDATION);
+        
+        collector.addFactory(
+                new SupportingMethodValidatorRefinerFactory(), FacetFactoryCategory.AFTER_BUILT_IN);
+        
     }
 
 }
diff --git a/extensions/incubator/src/main/resources/META-INF/services/org.apache.isis.metamodel.progmodel.ProgrammingModelPlugin b/extensions/incubator/src/main/resources/META-INF/services/org.apache.isis.metamodel.progmodel.ProgrammingModelPlugin
index a38e67a..450593b 100644
--- a/extensions/incubator/src/main/resources/META-INF/services/org.apache.isis.metamodel.progmodel.ProgrammingModelPlugin
+++ b/extensions/incubator/src/main/resources/META-INF/services/org.apache.isis.metamodel.progmodel.ProgrammingModelPlugin
@@ -1 +1 @@
-org.apache.isis.extensions.incubator.SupportingMethodPlugin
\ No newline at end of file
+org.apache.isis.extensions.incubator.IncubatorPlugin
\ No newline at end of file
diff --git a/extensions/markdown/src/main/java/org/apache/isis/extensions/markdown/MarkdownComponent.java b/extensions/markdown/src/main/java/org/apache/isis/extensions/markdown/MarkdownComponent.java
index f5135c6..edce457 100644
--- a/extensions/markdown/src/main/java/org/apache/isis/extensions/markdown/MarkdownComponent.java
+++ b/extensions/markdown/src/main/java/org/apache/isis/extensions/markdown/MarkdownComponent.java
@@ -38,7 +38,7 @@ public class MarkdownComponent extends MarkupComponent {
     private static final long serialVersionUID = 1L;
 
     public MarkdownComponent(String id, IModel<?> model) {
-        super(id, model, /*observing*/ null);
+        super(id, model);
     }
 
     @Override
diff --git a/extensions/pom.xml b/extensions/pom.xml
index 5b921c5..ded45f2 100644
--- a/extensions/pom.xml
+++ b/extensions/pom.xml
@@ -216,14 +216,16 @@
 
 	<modules>
 		<module>asciidoc</module>
-		<module>markdown</module>
 		<module>fixtures</module>
+		<module>markdown</module>
+		<module>restclient</module>
+		<module>secman</module>
 		<module>specsupport</module>
+		<module>sse</module>
+		<module>viewer-wicket-excel</module>
+
 		<module>incubator</module>
 		<module>legacy</module>
-		<module>viewer-wicket-excel</module>
-		<module>secman</module>
-		<module>restclient</module>
 	</modules>
 
 </project>
diff --git a/extensions/sse/_adoc/antora.yml b/extensions/sse/_adoc/antora.yml
new file mode 100644
index 0000000..9e8e614
--- /dev/null
+++ b/extensions/sse/_adoc/antora.yml
@@ -0,0 +1,6 @@
+name: ext-sse
+title: "Extensions - Server Sent Events"
+version: master
+start_page: ROOT:about.adoc
+nav:
+- modules/ROOT/nav.adoc
diff --git a/extensions/sse/_adoc/examples.csv b/extensions/sse/_adoc/examples.csv
new file mode 100644
index 0000000..5d5804b
--- /dev/null
+++ b/extensions/sse/_adoc/examples.csv
@@ -0,0 +1 @@
+#file,source,target
diff --git a/extensions/sse/_adoc/modules/ROOT/_attributes.adoc b/extensions/sse/_adoc/modules/ROOT/_attributes.adoc
new file mode 100644
index 0000000..787e5c4
--- /dev/null
+++ b/extensions/sse/_adoc/modules/ROOT/_attributes.adoc
@@ -0,0 +1,6 @@
+ifndef::env-site,env-github[]
+:attachmentsdir: {moduledir}/assets/attachments
+:examplesdir: {moduledir}/examples
+:imagesdir: {moduledir}/assets/images
+:partialsdir: {moduledir}/pages/_partials
+endif::[]
diff --git a/extensions/sse/_adoc/modules/ROOT/assets/attachments/.gitkeep b/extensions/sse/_adoc/modules/ROOT/assets/attachments/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/extensions/sse/_adoc/modules/ROOT/assets/images/.gitkeep b/extensions/sse/_adoc/modules/ROOT/assets/images/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/extensions/sse/_adoc/modules/ROOT/examples/.gitkeep b/extensions/sse/_adoc/modules/ROOT/examples/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/extensions/sse/_adoc/modules/ROOT/nav.adoc b/extensions/sse/_adoc/modules/ROOT/nav.adoc
new file mode 100644
index 0000000..2815342
--- /dev/null
+++ b/extensions/sse/_adoc/modules/ROOT/nav.adoc
@@ -0,0 +1,2 @@
+* xref:about.adoc[About]
+
diff --git a/extensions/sse/_adoc/modules/ROOT/pages/_attributes.adoc b/extensions/sse/_adoc/modules/ROOT/pages/_attributes.adoc
new file mode 100644
index 0000000..e8ada7c
--- /dev/null
+++ b/extensions/sse/_adoc/modules/ROOT/pages/_attributes.adoc
@@ -0,0 +1,4 @@
+ifndef::env-site,env-github[]
+:moduledir: ..
+include::{moduledir}/_attributes.adoc[]
+endif::[]
diff --git a/extensions/sse/_adoc/modules/ROOT/pages/_partials/_attributes.adoc b/extensions/sse/_adoc/modules/ROOT/pages/_partials/_attributes.adoc
new file mode 100644
index 0000000..d011536
--- /dev/null
+++ b/extensions/sse/_adoc/modules/ROOT/pages/_partials/_attributes.adoc
@@ -0,0 +1,4 @@
+ifndef::env-site,env-github[]
+:moduledir: ../..
+include::{moduledir}/_attributes.adoc[]
+endif::[]
diff --git a/extensions/sse/_adoc/modules/ROOT/pages/about.adoc b/extensions/sse/_adoc/modules/ROOT/pages/about.adoc
new file mode 100644
index 0000000..ead73b5
--- /dev/null
+++ b/extensions/sse/_adoc/modules/ROOT/pages/about.adoc
@@ -0,0 +1,6 @@
+= Markdown Extension
+:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
+include::_attributes.adoc[]
+
+TODO: v2: placeholder for documentation about extensions/markdown
+
diff --git a/extensions/sse/_adoc/sync_examples.sh b/extensions/sse/_adoc/sync_examples.sh
new file mode 100644
index 0000000..5260cd7
--- /dev/null
+++ b/extensions/sse/_adoc/sync_examples.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+SCRIPT=$(readlink -f "$0")
+SCRIPTPATH=$(dirname "$SCRIPT")
+if [ $# -ne 1 ]; then
+    FILELIST=$SCRIPTPATH/examples.csv
+else
+    FILELIST=$1
+fi
+FILELISTFQN=$(readlink -f $FILELIST)
+DIRNAME=$(dirname $FILELISTFQN)
+BASENAME=$(basename $FILELISTFQN)
+
+cd $DIRNAME >/dev/null 2>&1
+cat $BASENAME | tail +2 | grep -v ^# | while read LINE
+do
+    FILE=$(echo $LINE | awk -F, '{print $1}' | awk '{$1=$1;print}')
+    F2=$(echo $LINE | awk -F, '{print $2}' | awk '{$1=$1;print}')
+    F3=$(echo $LINE | awk -F, '{print $3}' | awk '{$1=$1;print}')
+    SOURCEFQN=$DIRNAME/$F2/$FILE
+    TARGETDIR=$DIRNAME/$F3
+    TARGETFQN=$DIRNAME/$F3/$FILE
+
+    echo "SOURCEFQN: $SOURCEFQN"
+
+    if [ -f "$SOURCEFQN" ]
+    then
+
+        if [ -f "$TARGETFQN" ]
+        then
+            rm $TARGETFQN
+        fi
+        mkdir -p $TARGETDIR
+        cp $SOURCEFQN $TARGETFQN
+    else
+        echo "- does not exist ($SOURCEFQN) " >&2
+        exit 1
+    fi
+done
+cd - >/dev/null 2>&1
\ No newline at end of file
diff --git a/extensions/incubator/pom.xml b/extensions/sse/pom.xml
similarity index 81%
copy from extensions/incubator/pom.xml
copy to extensions/sse/pom.xml
index 03d3af4..cfc1453 100644
--- a/extensions/incubator/pom.xml
+++ b/extensions/sse/pom.xml
@@ -21,27 +21,33 @@
 		<relativePath>../pom.xml</relativePath>
 	</parent>
 
-	<artifactId>isis-extensions-incubator</artifactId>
-	<name>Apache Isis Extensions - Incubator</name>
+	<artifactId>isis-extensions-sse</artifactId>
+	<name>Apache Isis Extensions - Server Sent Events</name>
 	<description></description>
 
 	<properties>
-		<jar-plugin.automaticModuleName>org.apache.isis.extensions.incubator</jar-plugin.automaticModuleName>
-		<git-plugin.propertiesDir>org/apache/isis/extensions/incubator</git-plugin.propertiesDir>
+		<jar-plugin.automaticModuleName>org.apache.isis.extensions.sse</jar-plugin.automaticModuleName>
+		<git-plugin.propertiesDir>org/apache/isis/extensions/sse</git-plugin.propertiesDir>
 	</properties>
 
 	<dependencies>
-	
+
 		<dependency>
 			<groupId>org.apache.isis.core</groupId>
 			<artifactId>isis-metamodel</artifactId>
 		</dependency>
-		
+
+		<dependency>
+			<groupId>org.apache.isis.core</groupId>
+			<artifactId>isis-runtime-web</artifactId>
+			<scope>provided</scope>
+		</dependency>
+
 		<dependency>
-            <groupId>org.apache.isis.core</groupId>
-            <artifactId>isis-viewer-wicket-ui</artifactId>
-            <scope>provided</scope>
-        </dependency>
+			<groupId>org.apache.isis.core</groupId>
+			<artifactId>isis-viewer-wicket-ui</artifactId>
+			<scope>provided</scope>
+		</dependency>
 
 	</dependencies>
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/objectvalue/observe/ObserveFacet.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/IsisBootSse.java
similarity index 65%
copy from core/metamodel/src/main/java/org/apache/isis/metamodel/facets/objectvalue/observe/ObserveFacet.java
copy to extensions/sse/src/main/java/org/apache/isis/extensions/sse/IsisBootSse.java
index e20659a..6889fd6 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/objectvalue/observe/ObserveFacet.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/IsisBootSse.java
@@ -16,17 +16,19 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
+package org.apache.isis.extensions.sse;
 
-package org.apache.isis.metamodel.facets.objectvalue.observe;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
 
-import org.apache.isis.applib.events.sse.EventStreamSource;
-import org.apache.isis.metamodel.facets.SingleClassValueFacet;
+import org.apache.isis.extensions.sse.services.EventStreamServiceDefault;
+import org.apache.isis.extensions.sse.webapp.WebModuleServerSentEvents;
 
-/**
- * Corresponds to <tt>@Property(observe=...)</tt> annotation in the Isis programming model.
- */
-public interface ObserveFacet extends SingleClassValueFacet {
-
-    Class<? extends EventStreamSource> getEventStreamType();
+@Configuration
+@Import({
+    EventStreamServiceDefault.class,
+    WebModuleServerSentEvents.class
+})
+public class IsisBootSse {
 
 }
diff --git a/extensions/incubator/src/main/java/org/apache/isis/extensions/incubator/SupportingMethodPlugin.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/SsePlugin.java
similarity index 74%
rename from extensions/incubator/src/main/java/org/apache/isis/extensions/incubator/SupportingMethodPlugin.java
rename to extensions/sse/src/main/java/org/apache/isis/extensions/sse/SsePlugin.java
index 20cde3b..a26ed0a 100644
--- a/extensions/incubator/src/main/java/org/apache/isis/extensions/incubator/SupportingMethodPlugin.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/SsePlugin.java
@@ -16,16 +16,19 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.extensions.incubator;
+package org.apache.isis.extensions.sse;
 
-import org.apache.isis.metamodel.facets.actions.support.SupportingMethodValidatorRefinerFactory;
+import org.apache.isis.extensions.sse.facets.SseAnnotationFacetFactory;
 import org.apache.isis.metamodel.progmodel.ProgrammingModelPlugin;
 
-public class SupportingMethodPlugin implements ProgrammingModelPlugin {
+public class SsePlugin implements ProgrammingModelPlugin {
 
     @Override
     public void plugin(FactoryCollector collector) {
-        collector.addFactory(new SupportingMethodValidatorRefinerFactory(), FacetFactoryCategory.VALIDATION);
+        
+        collector.addFactory(
+                new SseAnnotationFacetFactory(), FacetFactoryCategory.AFTER_BUILT_IN);
+        
     }
 
 }
diff --git a/core/applib/src/main/java/org/apache/isis/applib/events/sse/EventStreamService.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/api/ServerSentEvents.java
similarity index 63%
copy from core/applib/src/main/java/org/apache/isis/applib/events/sse/EventStreamService.java
copy to extensions/sse/src/main/java/org/apache/isis/extensions/sse/api/ServerSentEvents.java
index 37630df..bef3e39 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/events/sse/EventStreamService.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/api/ServerSentEvents.java
@@ -16,27 +16,21 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.events.sse;
+package org.apache.isis.extensions.sse.api;
 
-import java.util.Optional;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
-/**
- * Server-sent events.
- *  
- * @see https://www.w3schools.com/html/html5_serversentevents.asp
- * 
- * @since 2.0
- *
- */
-public interface EventStreamService {
-
-    public static enum ExecutionBehavior {
-        SIMPLE,
-        REQUIRES_NEW_SESSION
-    }
-
-    Optional<EventStream> lookupByType(Class<?> sourceType);
-
-    void submit(EventStreamSource task, ExecutionBehavior executionBehavior);
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ServerSentEvents {
+    
+    /**
+     * TODO
+     *
+     */
+    Class<? extends SseSource> observe() default SseSource.Noop.class;
 
 }
diff --git a/core/applib/src/main/java/org/apache/isis/applib/events/sse/EventStream.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/api/SseChannel.java
similarity index 86%
rename from core/applib/src/main/java/org/apache/isis/applib/events/sse/EventStream.java
rename to extensions/sse/src/main/java/org/apache/isis/extensions/sse/api/SseChannel.java
index 0115330..a8c3b19 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/events/sse/EventStream.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/api/SseChannel.java
@@ -17,7 +17,7 @@
  *  under the License.
  */
 
-package org.apache.isis.applib.events.sse;
+package org.apache.isis.extensions.sse.api;
 
 import java.util.UUID;
 import java.util.function.Predicate;
@@ -30,14 +30,14 @@ import java.util.function.Predicate;
  * @since 2.0
  *
  */
-public interface EventStream {
+public interface SseChannel {
 
     UUID getId();
     Class<?> getSourceType();
 
-    void listenWhile(Predicate<EventStreamSource> listener);
+    void listenWhile(Predicate<SseSource> listener);
 
-    void fire(EventStreamSource source);
+    void fire(SseSource source);
 
     void close();
 
diff --git a/core/applib/src/main/java/org/apache/isis/applib/events/sse/EventStreamService.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/api/SseService.java
similarity index 83%
rename from core/applib/src/main/java/org/apache/isis/applib/events/sse/EventStreamService.java
rename to extensions/sse/src/main/java/org/apache/isis/extensions/sse/api/SseService.java
index 37630df..c81c28c 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/events/sse/EventStreamService.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/api/SseService.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.events.sse;
+package org.apache.isis.extensions.sse.api;
 
 import java.util.Optional;
 
@@ -28,15 +28,15 @@ import java.util.Optional;
  * @since 2.0
  *
  */
-public interface EventStreamService {
+public interface SseService {
 
     public static enum ExecutionBehavior {
         SIMPLE,
         REQUIRES_NEW_SESSION
     }
 
-    Optional<EventStream> lookupByType(Class<?> sourceType);
+    Optional<SseChannel> lookupByType(Class<?> sourceType);
 
-    void submit(EventStreamSource task, ExecutionBehavior executionBehavior);
+    void submit(SseSource task, ExecutionBehavior executionBehavior);
 
 }
diff --git a/core/applib/src/main/java/org/apache/isis/applib/events/sse/EventStreamSource.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/api/SseSource.java
similarity index 72%
rename from core/applib/src/main/java/org/apache/isis/applib/events/sse/EventStreamSource.java
rename to extensions/sse/src/main/java/org/apache/isis/extensions/sse/api/SseSource.java
index 8d67184..ed13cf9 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/events/sse/EventStreamSource.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/api/SseSource.java
@@ -17,9 +17,7 @@
  *  under the License.
  */
 
-package org.apache.isis.applib.events.sse;
-
-import org.apache.isis.applib.value.Markup;
+package org.apache.isis.extensions.sse.api;
 
 /**
  * Server-sent events.
@@ -29,28 +27,28 @@ import org.apache.isis.applib.value.Markup;
  * @since 2.0
  *
  */
-public interface EventStreamSource {
+public interface SseSource {
 
-    void run(EventStream eventStream);
+    void run(SseChannel channel);
 
-    Markup getPayload();
+    String getPayload();
 
     // -- PROPERTY ANNOTATION DEFAULT
 
     /**
      * This class is the default for the
-     * {@link org.apache.isis.applib.annotation.Property#observe()} annotation attribute.  
+     * {@link ServerSentEvents#observe()} annotation attribute.  
      */
-    public static final class Noop implements EventStreamSource {
+    public static final class Noop implements SseSource {
 
         @Override
-        public void run(EventStream eventStream) {
+        public void run(SseChannel eventStream) {
             // just do nothing
         }
 
         @Override
-        public Markup getPayload() {
-            return new Markup();
+        public String getPayload() {
+            return "";
         }
 
     }
@@ -61,7 +59,7 @@ public interface EventStreamSource {
         if(type==null) {
             return false;
         }
-        if(!EventStreamSource.class.isAssignableFrom(type)) {
+        if(!SseSource.class.isAssignableFrom(type)) {
             return false;    
         }
         return !type.equals(Noop.class);
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/objectvalue/observe/ObserveFacet.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/facets/ObserveFacet.java
similarity index 63%
rename from core/metamodel/src/main/java/org/apache/isis/metamodel/facets/objectvalue/observe/ObserveFacet.java
rename to extensions/sse/src/main/java/org/apache/isis/extensions/sse/facets/ObserveFacet.java
index e20659a..facb0e7 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/objectvalue/observe/ObserveFacet.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/facets/ObserveFacet.java
@@ -17,16 +17,23 @@
  *  under the License.
  */
 
-package org.apache.isis.metamodel.facets.objectvalue.observe;
+package org.apache.isis.extensions.sse.facets;
 
-import org.apache.isis.applib.events.sse.EventStreamSource;
+import org.apache.isis.applib.value.LocalResourcePath;
+import org.apache.isis.extensions.sse.api.SseSource;
 import org.apache.isis.metamodel.facets.SingleClassValueFacet;
 
 /**
- * Corresponds to <tt>@Property(observe=...)</tt> annotation in the Isis programming model.
+ * Corresponds to <tt>@???(observe=...)</tt> annotation in the Isis programming model.
  */
 public interface ObserveFacet extends SingleClassValueFacet {
 
-    Class<? extends EventStreamSource> getEventStreamType();
+    Class<? extends SseSource> getEventStreamType();
+    
+    default LocalResourcePath getEventStreamResource() {
+        final String eventStreamId = getEventStreamType().getName();
+        final LocalResourcePath ssePath = new LocalResourcePath("/sse?eventStream=" + eventStreamId);
+        return ssePath;
+    }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/objectvalue/observe/ObserveFacetAbstract.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/facets/ObserveFacetAbstract.java
similarity index 84%
rename from core/metamodel/src/main/java/org/apache/isis/metamodel/facets/objectvalue/observe/ObserveFacetAbstract.java
rename to extensions/sse/src/main/java/org/apache/isis/extensions/sse/facets/ObserveFacetAbstract.java
index 7a43bdd..9c5b49b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/objectvalue/observe/ObserveFacetAbstract.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/facets/ObserveFacetAbstract.java
@@ -17,10 +17,10 @@
  *  under the License.
  */
 
-package org.apache.isis.metamodel.facets.objectvalue.observe;
+package org.apache.isis.extensions.sse.facets;
 
-import org.apache.isis.applib.events.sse.EventStreamSource;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
+import org.apache.isis.extensions.sse.api.SseSource;
 import org.apache.isis.metamodel.facetapi.Facet;
 import org.apache.isis.metamodel.facetapi.FacetHolder;
 import org.apache.isis.metamodel.facets.SingleClassValueFacetAbstract;
@@ -28,14 +28,14 @@ import org.apache.isis.metamodel.spec.ObjectSpecification;
 
 public abstract class ObserveFacetAbstract extends SingleClassValueFacetAbstract implements ObserveFacet {
 
-    private Class<? extends EventStreamSource> eventStreamType;
+    private Class<? extends SseSource> eventStreamType;
 
     public static Class<? extends Facet> type() {
         return ObserveFacet.class;
     }
 
     public ObserveFacetAbstract(
-            final Class<? extends EventStreamSource> eventStreamType,
+            final Class<? extends SseSource> eventStreamType,
             final FacetHolder holder) {
 
         super(type(), holder, eventStreamType);
@@ -48,7 +48,7 @@ public abstract class ObserveFacetAbstract extends SingleClassValueFacetAbstract
     }
 
     @Override
-    public Class<? extends EventStreamSource> getEventStreamType() {
+    public Class<? extends SseSource> getEventStreamType() {
         return eventStreamType;
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/properties/property/observe/ObserveFacetForPropertyAnnotation.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/facets/ObserveFacetForSseAnnotation.java
similarity index 62%
rename from core/metamodel/src/main/java/org/apache/isis/metamodel/facets/properties/property/observe/ObserveFacetForPropertyAnnotation.java
rename to extensions/sse/src/main/java/org/apache/isis/extensions/sse/facets/ObserveFacetForSseAnnotation.java
index f533506..996427f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/properties/property/observe/ObserveFacetForPropertyAnnotation.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/facets/ObserveFacetForSseAnnotation.java
@@ -17,38 +17,36 @@
  *  under the License.
  */
 
-package org.apache.isis.metamodel.facets.properties.property.observe;
+package org.apache.isis.extensions.sse.facets;
 
 import java.util.List;
 
-import org.apache.isis.applib.annotation.Property;
-import org.apache.isis.applib.events.sse.EventStreamSource;
+import org.apache.isis.extensions.sse.api.SseSource;
+import org.apache.isis.extensions.sse.api.ServerSentEvents;
 import org.apache.isis.metamodel.facetapi.FacetHolder;
-import org.apache.isis.metamodel.facets.objectvalue.observe.ObserveFacet;
-import org.apache.isis.metamodel.facets.objectvalue.observe.ObserveFacetAbstract;
 
 /**
  * 
  * @since 2.0
  *
  */
-public class ObserveFacetForPropertyAnnotation extends ObserveFacetAbstract {
+public class ObserveFacetForSseAnnotation extends ObserveFacetAbstract {
 
     public static ObserveFacet create(
-            final List<Property> properties,
+            final List<ServerSentEvents> properties,
             final FacetHolder holder) {
 
         return properties.stream()
-                .map(Property::observe)
-                .filter(EventStreamSource::isObservable)
+                .map(ServerSentEvents::observe)
+                .filter(SseSource::isObservable)
                 .findFirst()
-                .map(eventStreamType -> new ObserveFacetForPropertyAnnotation(
+                .map(eventStreamType -> new ObserveFacetForSseAnnotation(
                         eventStreamType, holder))
                 .orElse(null);
     }
 
-    private ObserveFacetForPropertyAnnotation(
-            Class<? extends EventStreamSource> eventStreamType, 
+    private ObserveFacetForSseAnnotation(
+            Class<? extends SseSource> eventStreamType, 
             FacetHolder holder) {
 
         super(eventStreamType, holder);
diff --git a/extensions/sse/src/main/java/org/apache/isis/extensions/sse/facets/SseAnnotationFacetFactory.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/facets/SseAnnotationFacetFactory.java
new file mode 100644
index 0000000..1e909b4
--- /dev/null
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/facets/SseAnnotationFacetFactory.java
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.extensions.sse.facets;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.apache.isis.extensions.sse.api.ServerSentEvents;
+import org.apache.isis.metamodel.facetapi.FacetHolder;
+import org.apache.isis.metamodel.facetapi.FacetUtil;
+import org.apache.isis.metamodel.facetapi.FeatureType;
+import org.apache.isis.metamodel.facetapi.MetaModelValidatorRefiner;
+import org.apache.isis.metamodel.facets.Annotations;
+import org.apache.isis.metamodel.facets.FacetFactoryAbstract;
+import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorComposite;
+
+public class SseAnnotationFacetFactory extends FacetFactoryAbstract 
+implements MetaModelValidatorRefiner {
+
+    public SseAnnotationFacetFactory() {
+        super(FeatureType.PROPERTIES_ONLY);
+    }
+
+    @Override
+    public void process(final ProcessMethodContext processMethodContext) {
+        processObserve(processMethodContext);
+    }
+
+
+    void processObserve(final ProcessMethodContext processMethodContext) {
+        final Method method = processMethodContext.getMethod();
+        final FacetHolder holder = processMethodContext.getFacetHolder();
+
+        // else search for @???(observe=...)
+        final List<ServerSentEvents> annots = Annotations.getAnnotations(method, ServerSentEvents.class);
+        ObserveFacet facet = ObserveFacetForSseAnnotation.create(annots, holder);
+
+        FacetUtil.addFacet(facet);
+    }
+
+    @Override
+    public void refineMetaModelValidator(MetaModelValidatorComposite metaModelValidator) {
+    }
+
+
+}
diff --git a/extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/ListeningMarkup.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/ListeningMarkup.java
new file mode 100644
index 0000000..d03eb45
--- /dev/null
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/ListeningMarkup.java
@@ -0,0 +1,28 @@
+package org.apache.isis.extensions.sse.markup;
+
+import org.apache.isis.applib.annotation.Value;
+import org.apache.isis.applib.value.Markup;
+import org.apache.isis.extensions.sse.api.ServerSentEvents;
+
+/**
+ * Immutable value type holding pre-rendered HTML. Supports server sent events.
+ * <p>
+ * Annotate with {@link ServerSentEvents} to bind this to a channel to listen on.
+ *
+ */
+@Value(semanticsProviderName = 
+        "org.apache.isis.metamodel.facets.value.markup.MarkupValueSemanticsProvider")
+public class ListeningMarkup extends Markup {
+
+    private static final long serialVersionUID = 1L;
+
+    public static ListeningMarkup valueOfHtml(String html) {
+        return new ListeningMarkup(html);
+    }
+
+    private ListeningMarkup(String html) {
+        super(html);
+    }
+
+
+}
diff --git a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/ListeningMarkupComponent.java
similarity index 55%
copy from core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java
copy to extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/ListeningMarkupComponent.java
index 18c55d5..9a74eb4 100644
--- a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupComponent.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/ListeningMarkupComponent.java
@@ -16,32 +16,28 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-
-package org.apache.isis.viewer.wicket.ui.components.scalars.markup;
+package org.apache.isis.extensions.sse.markup;
 
 import org.apache.wicket.markup.ComponentTag;
 import org.apache.wicket.markup.MarkupStream;
-import org.apache.wicket.markup.html.WebComponent;
-import org.apache.wicket.markup.parser.XmlTag.TagType;
 import org.apache.wicket.model.IModel;
 
 import org.apache.isis.applib.value.LocalResourcePath;
-import org.apache.isis.applib.value.Markup;
-import org.apache.isis.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.viewer.wicket.ui.components.scalars.markup.MarkupComponent;
 
 import lombok.val;
 
-public class MarkupComponent extends WebComponent {
+public class ListeningMarkupComponent extends MarkupComponent {
 
     private static final long serialVersionUID = 1L;
-
+    
     private final LocalResourcePath observing;
-
-    public MarkupComponent(final String id, IModel<?> model, LocalResourcePath observing){
+    
+    public ListeningMarkupComponent(final String id, IModel<?> model, LocalResourcePath observing){
         super(id, model);
         this.observing = observing;
     }
-
+    
     @Override
     public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag){
         val htmlContent = extractHtmlOrElse(getDefaultModelObject(), "" /*fallback*/);
@@ -50,44 +46,11 @@ public class MarkupComponent extends WebComponent {
                 openTag, 
 
                 observing!=null 
-                ? MarkupComponent_observing.decorate(htmlContent, observing)
+                ? ListeningMarkupComponent_observing.decorate(htmlContent, observing)
                         : htmlContent
 
                 );
     }
 
-    @Override
-    protected void onComponentTag(ComponentTag tag)	{
-        super.onComponentTag(tag);
-        tag.setType(TagType.OPEN);
-    }
-
-    // -- HELPER
-
-    protected static CharSequence extractHtmlOrElse(Object modelObject, final String fallback) {
-
-        if(modelObject==null) {
-            return fallback;
-        }
-
-        if(modelObject instanceof ObjectAdapter) {
-
-            final ObjectAdapter objAdapter = (ObjectAdapter) modelObject;
-
-            if(objAdapter.getPojo()==null)
-                return fallback;
-
-            final Object value = objAdapter.getPojo();
-
-            if(!(value instanceof Markup))
-                return fallback;
-
-            return ((Markup)value).asString();
-        }
-
-        return modelObject.toString();
-
-    }
-
-
+    
 }
diff --git a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupComponent_observing.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/ListeningMarkupComponent_observing.java
similarity index 89%
rename from core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupComponent_observing.java
rename to extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/ListeningMarkupComponent_observing.java
index 41b6a11..2388a21 100644
--- a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupComponent_observing.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/ListeningMarkupComponent_observing.java
@@ -17,7 +17,7 @@
  *  under the License.
  */
 
-package org.apache.isis.viewer.wicket.ui.components.scalars.markup;
+package org.apache.isis.extensions.sse.markup;
 
 import java.io.IOException;
 import java.util.UUID;
@@ -27,9 +27,9 @@ import org.apache.isis.commons.internal.resources._Resources;
 
 import static org.apache.isis.commons.internal.base._Strings.isNullOrEmpty;
 
-final class MarkupComponent_observing  {
+final class ListeningMarkupComponent_observing  {
 
-    private static final String jScriptTemplateResource = "ObservingComponent.js";
+    private static final String jScriptTemplateResource = "js/ObservingComponent.js";
 
     static CharSequence decorate(CharSequence htmlContent, LocalResourcePath observing) {
         if(observing==null) {
@@ -38,7 +38,7 @@ final class MarkupComponent_observing  {
         final String jScriptTemplate;
         try {
             jScriptTemplate = _Resources.loadAsStringUtf8(
-                    MarkupComponent_observing.class, jScriptTemplateResource);
+                    ListeningMarkupComponent_observing.class, jScriptTemplateResource);
 
         } catch (IOException e) {
             e.printStackTrace();
diff --git a/extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/ListeningMarkupPanelFactoriesForWicket.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/ListeningMarkupPanelFactoriesForWicket.java
new file mode 100644
index 0000000..ddef1d4
--- /dev/null
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/ListeningMarkupPanelFactoriesForWicket.java
@@ -0,0 +1,90 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.extensions.sse.markup;
+
+import org.apache.isis.applib.value.LocalResourcePath;
+import org.apache.isis.extensions.sse.facets.ObserveFacet;
+import org.apache.isis.viewer.wicket.model.models.ScalarModel;
+import org.apache.isis.viewer.wicket.ui.components.scalars.markup.MarkupComponentFactory;
+import org.apache.isis.viewer.wicket.ui.components.scalars.markup.MarkupPanelFactories;
+
+import lombok.val;
+
+/**
+ * @implNote Almost a copy of {@code Parented} and {@code Standalone} in 
+ * {@link MarkupPanelFactories}, but specific to 
+ * the {@link ListeningMarkup} value-type which requires client-side java-script to be
+ * executed to enable syntax highlighting
+ */
+public class ListeningMarkupPanelFactoriesForWicket {
+
+    // -- PARENTED
+
+    public static class Parented extends MarkupPanelFactories.ParentedAbstract {
+        private static final long serialVersionUID = 1L;
+
+        public Parented() {
+            super(ListeningMarkup.class);
+        }
+
+        
+        @Override
+        protected MarkupComponentFactory getMarkupComponentFactory() {
+            return (id, model) -> {
+                val markupComponent = new ListeningMarkupComponent(
+                        id, model, getEventStreamResource((ScalarModel)model));
+                markupComponent.setEnabled(false);
+                return markupComponent;    
+            };
+        }
+
+        // -- HELPER
+
+        private LocalResourcePath getEventStreamResource(ScalarModel scalarModel) {
+            val observeFacet  = scalarModel.getFacet(ObserveFacet.class);
+            return observeFacet!=null
+                    ? observeFacet.getEventStreamResource()
+                            : null;
+        }
+        
+
+    }
+
+    // -- STANDALONE
+
+    public static class Standalone extends MarkupPanelFactories.StandaloneAbstract {
+        private static final long serialVersionUID = 1L;
+
+        public Standalone() {
+            super(ListeningMarkup.class);
+        }
+
+        @Override
+        protected MarkupComponentFactory getMarkupComponentFactory() {
+            return (id, model) -> {
+                val markupComponent = new ListeningMarkupComponent(id, model, /*observing*/ null);
+                return markupComponent;    
+            };
+        }
+
+    }
+
+
+}
diff --git a/core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/ObservingComponent.js b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/js/ObservingComponent.js
similarity index 100%
rename from core/viewer-wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/ObservingComponent.js
rename to extensions/sse/src/main/java/org/apache/isis/extensions/sse/markup/js/ObservingComponent.js
diff --git a/core/runtime-services/src/main/java/org/apache/isis/runtime/services/sse/EventStreamServiceDefault.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/services/EventStreamServiceDefault.java
similarity index 86%
rename from core/runtime-services/src/main/java/org/apache/isis/runtime/services/sse/EventStreamServiceDefault.java
rename to extensions/sse/src/main/java/org/apache/isis/extensions/sse/services/EventStreamServiceDefault.java
index a9a6483..93a6215 100644
--- a/core/runtime-services/src/main/java/org/apache/isis/runtime/services/sse/EventStreamServiceDefault.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/services/EventStreamServiceDefault.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.runtime.services.sse;
+package org.apache.isis.extensions.sse.services;
 
 import java.util.List;
 import java.util.Map;
@@ -35,11 +35,11 @@ import javax.inject.Inject;
 
 import org.springframework.stereotype.Service;
 
-import org.apache.isis.applib.events.sse.EventStream;
-import org.apache.isis.applib.events.sse.EventStreamService;
-import org.apache.isis.applib.events.sse.EventStreamSource;
 import org.apache.isis.applib.services.xactn.TransactionService;
 import org.apache.isis.commons.internal.collections._Lists;
+import org.apache.isis.extensions.sse.api.SseChannel;
+import org.apache.isis.extensions.sse.api.SseService;
+import org.apache.isis.extensions.sse.api.SseSource;
 import org.apache.isis.runtime.system.context.IsisContext;
 import org.apache.isis.runtime.system.transaction.IsisTransactionAspectSupport;
 
@@ -58,19 +58,19 @@ import lombok.extern.log4j.Log4j2;
  *
  */
 @Service @Log4j2
-public class EventStreamServiceDefault implements EventStreamService {
+public class EventStreamServiceDefault implements SseService {
 
     @Inject TransactionService transactionService;
 
     private final EventStreamPool eventStreamPool = new EventStreamPool();
 
     @Override
-    public Optional<EventStream> lookupByType(Class<?> sourceType) {
+    public Optional<SseChannel> lookupByType(Class<?> sourceType) {
         return eventStreamPool.lookupByType(sourceType);
     }
 
     @Override
-    public void submit(EventStreamSource task, ExecutionBehavior executionBehavior) {
+    public void submit(SseSource task, ExecutionBehavior executionBehavior) {
 
         Objects.requireNonNull(task);
         Objects.requireNonNull(executionBehavior);
@@ -101,7 +101,7 @@ public class EventStreamServiceDefault implements EventStreamService {
 
     // -- HELPER
 
-    private void run(EventStreamSource task) {
+    private void run(SseSource task) {
 
         val sourceType = task.getClass();
 
@@ -131,7 +131,7 @@ public class EventStreamServiceDefault implements EventStreamService {
 
         private final Map<Class<?>,  EventStreamLifecycle> eventStreamsByType = new ConcurrentHashMap<>();
 
-        public Optional<EventStream> lookupByType(Class<?> sourceType) {
+        public Optional<SseChannel> lookupByType(Class<?> sourceType) {
             return Optional.ofNullable(eventStreamsByType.get(sourceType))
                     .map(EventStreamLifecycle::getEventStream);
         }
@@ -154,7 +154,7 @@ public class EventStreamServiceDefault implements EventStreamService {
 
         private static final Object $LOCK = new Object[0]; //see https://projectlombok.org/features/Synchronized
 
-        @Getter private final EventStream eventStream;
+        @Getter private final SseChannel eventStream;
         private final EventStreamPool eventStreamPool;
 
         private int runningTasksCounter;
@@ -189,7 +189,7 @@ public class EventStreamServiceDefault implements EventStreamService {
     // -- EVENT STREAM DEFAULT IMPLEMENTATION
 
     @Value @Log4j2
-    private static class EventStreamDefault implements EventStream {
+    private static class EventStreamDefault implements SseChannel {
 
         private static final Object $LOCK = new Object[0]; //see https://projectlombok.org/features/Synchronized
 
@@ -197,12 +197,12 @@ public class EventStreamServiceDefault implements EventStreamService {
         @Getter final Class<?> sourceType;
 
         private final CountDownLatch latch = new CountDownLatch(1);
-        private final Queue<Predicate<EventStreamSource>> listeners = new ConcurrentLinkedQueue<>();
+        private final Queue<Predicate<SseSource>> listeners = new ConcurrentLinkedQueue<>();
 
         @Override
-        public void fire(EventStreamSource source) {
+        public void fire(SseSource source) {
 
-            final List<Predicate<EventStreamSource>> defensiveCopyOfListeners;
+            final List<Predicate<SseSource>> defensiveCopyOfListeners;
 
             synchronized ($LOCK) {
                 if(!isActive()) {
@@ -214,7 +214,7 @@ public class EventStreamServiceDefault implements EventStreamService {
 
             log.debug("about to fire events to {} listeners", ()->defensiveCopyOfListeners.size());
 
-            final List<Predicate<EventStreamSource>> markedForRemoval = _Lists.newArrayList();
+            final List<Predicate<SseSource>> markedForRemoval = _Lists.newArrayList();
 
             defensiveCopyOfListeners.forEach(listener->{
                 val retain = listener.test(source);
@@ -234,7 +234,7 @@ public class EventStreamServiceDefault implements EventStreamService {
         }
 
         @Override
-        public void listenWhile(Predicate<EventStreamSource> listener) {
+        public void listenWhile(Predicate<SseSource> listener) {
             synchronized ($LOCK) {
                 if(isActive()) {
                     listeners.add(listener);   
diff --git a/core/runtime-web/src/main/java/org/apache/isis/webapp/modules/sse/ServerSentEventsServlet.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/webapp/ServerSentEventsServlet.java
similarity index 90%
rename from core/runtime-web/src/main/java/org/apache/isis/webapp/modules/sse/ServerSentEventsServlet.java
rename to extensions/sse/src/main/java/org/apache/isis/extensions/sse/webapp/ServerSentEventsServlet.java
index df4e046..1366f5f 100644
--- a/core/runtime-web/src/main/java/org/apache/isis/webapp/modules/sse/ServerSentEventsServlet.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/webapp/ServerSentEventsServlet.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.webapp.modules.sse;
+package org.apache.isis.extensions.sse.webapp;
 
 import java.io.IOException;
 import java.util.Optional;
@@ -29,12 +29,13 @@ import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.isis.applib.events.sse.EventStream;
-import org.apache.isis.applib.events.sse.EventStreamService;
 import org.apache.isis.applib.util.JaxbAdapters;
 import org.apache.isis.commons.internal.base._Lazy;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.context._Context;
+import org.apache.isis.extensions.sse.api.SseChannel;
+import org.apache.isis.extensions.sse.api.SseService;
+import org.apache.isis.extensions.sse.markup.ListeningMarkup;
 import org.apache.isis.runtime.system.context.IsisContext;
 
 import lombok.val;
@@ -52,7 +53,7 @@ public class ServerSentEventsServlet extends HttpServlet {
     private static final long serialVersionUID = 1L;
     private ExecutorService threadPool;
 
-    private final _Lazy<EventStreamService> eventStreamService_Lazy =
+    private final _Lazy<SseService> eventStreamService_Lazy =
             _Lazy.threadSafe(this::lookupEventStreamService);
 
     @Override
@@ -98,7 +99,7 @@ public class ServerSentEventsServlet extends HttpServlet {
 
     // -- HELPER
 
-    private void fork(final AsyncContext asyncContext, final EventStream eventStream) {
+    private void fork(final AsyncContext asyncContext, final SseChannel eventStream) {
 
         val response = asyncContext.getResponse();
         val marshaller = new JaxbAdapters.MarkupAdapter();
@@ -116,7 +117,7 @@ public class ServerSentEventsServlet extends HttpServlet {
                     return false; // stop listening
                 }
 
-                val payload = marshaller.marshal(source.getPayload());
+                val payload = marshaller.marshal(ListeningMarkup.valueOfHtml(source.getPayload()));
 
                 writer
                 .append("data: ")
@@ -158,8 +159,8 @@ public class ServerSentEventsServlet extends HttpServlet {
         }
     }
 
-    private EventStreamService lookupEventStreamService() {
-        return IsisContext.getServiceRegistry().lookupServiceElseFail(EventStreamService.class);
+    private SseService lookupEventStreamService() {
+        return IsisContext.getServiceRegistry().lookupServiceElseFail(SseService.class);
     }
 
 }
diff --git a/core/runtime-web/src/main/java/org/apache/isis/webapp/modules/sse/WebModuleServerSentEvents.java b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/webapp/WebModuleServerSentEvents.java
similarity index 97%
rename from core/runtime-web/src/main/java/org/apache/isis/webapp/modules/sse/WebModuleServerSentEvents.java
rename to extensions/sse/src/main/java/org/apache/isis/extensions/sse/webapp/WebModuleServerSentEvents.java
index 036bcdd..5951e38 100644
--- a/core/runtime-web/src/main/java/org/apache/isis/webapp/modules/sse/WebModuleServerSentEvents.java
+++ b/extensions/sse/src/main/java/org/apache/isis/extensions/sse/webapp/WebModuleServerSentEvents.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.webapp.modules.sse;
+package org.apache.isis.extensions.sse.webapp;
 
 import javax.servlet.ServletContext;
 import javax.servlet.ServletContextListener;
diff --git a/extensions/sse/src/main/resources/META-INF/services/org.apache.isis.metamodel.progmodel.ProgrammingModelPlugin b/extensions/sse/src/main/resources/META-INF/services/org.apache.isis.metamodel.progmodel.ProgrammingModelPlugin
new file mode 100644
index 0000000..a1f7aab
--- /dev/null
+++ b/extensions/sse/src/main/resources/META-INF/services/org.apache.isis.metamodel.progmodel.ProgrammingModelPlugin
@@ -0,0 +1 @@
+org.apache.isis.extensions.sse.SsePlugin
\ No newline at end of file
diff --git a/extensions/sse/src/main/resources/META-INF/services/org.apache.isis.viewer.wicket.ui.ComponentFactory b/extensions/sse/src/main/resources/META-INF/services/org.apache.isis.viewer.wicket.ui.ComponentFactory
new file mode 100644
index 0000000..47a186b
--- /dev/null
+++ b/extensions/sse/src/main/resources/META-INF/services/org.apache.isis.viewer.wicket.ui.ComponentFactory
@@ -0,0 +1,2 @@
+org.apache.isis.extensions.sse.markup.ListeningMarkupPanelFactoriesForWicket$Parented
+org.apache.isis.extensions.sse.markup.ListeningMarkupPanelFactoriesForWicket$Standalone
\ No newline at end of file