Real-Time Collaborative Authoring in AEM

with WebSockets and Netty

Who Am I?

Mark Szumowski

Overview

  • Motivation
  • Demo
  • Code + Design
    • OSGi WebSocket Server
    • RTCA App
      • Back-end Service + EventHandler
      • Front-end authoring JS
  • Production Ready?
  • Future Extensions

Motivation

See what's going live when you hit publish

Avoid conflicts and lost work

Demo

Code + Design in 2 Parts

  1. OSGi WebSocket server + API
  2. Real-Time Collaborative Authoring App
    (services and front-end)

Java WebSocket Options

  • Jetty

    ... uses javax.servlet 3.0

  • JSR 356 (Java EE 7 WebSockets)

    ... Java EE in Felix looks unpleasant

  • Netty

    ... it's in the title of the talk

What is Netty?

“Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.” -- netty.io

... or a bunch of useful abstractions over the websocket protocol, a request pipeline, etc.

Why Netty?

  • Well designed API
  • Thorough documentation
  • Active development community
    (GitHub, Stack Overflow)
  • Ships as OSGi bundles

Heart of the Server

final ServerBootstrap sb = new ServerBootstrap();

sb.group(bossGroup, workerGroup)
  .channel(NioServerSocketChannel.class)
  .childHandler(new ChannelInitializer<SocketChannel>() {

      @Override
      public void initChannel(final SocketChannel ch) throws Exception {
          ch.pipeline()
            .addLast("dec", new HttpRequestDecoder())
            .addLast("agg", new HttpObjectAggregator(65536))
            .addLast("enc", new HttpResponseEncoder())
            .addLast("auth", slingAuthHandler)  // OUR CODE
            .addLast("dispatcher", dispatcher); // OUR CODE
      }

  });
					

Authentication

Need to bridge APIs

io.netty.handler.codec.http.HttpRequest

javax.servlet.http.HttpServletRequest
@Reference
private AuthenticationSupport auth;


protected void channelRead0(ChannelHandlerContext ctx, HttpRequest req)
        throws Exception {

    WrappedNettyHttpRequest authReq = new WrappedNettyHttpRequest(req);
    Attribute<ResourceResolver> resolver = ctx.channel().attr(RESOLVER_ATTR_KEY);

    //TODO: make sure this won't try to issue redirects via the null response param
    if (auth.handleSecurity(authReq, null)) {
        LOG.debug("Authentication successful.");
        resolver.set((ResourceResolver) authReq.getAttribute(
                     AuthenticationSupport.REQUEST_ATTRIBUTE_RESOLVER));
    } else {
        LOG.debug("Authentication failed - using anonymous resolver.");
        resolver.set(resourceResolverFactory.getResourceResolver(null));
    }
    ctx.fireChannelRead(req);
}
						

RTCA App

(RTCA: Real Time Collaborative Authoring)

  • RTCAService: Connection Aggregation Service
  • RTCAListener: Page Edit Listener

Connection Aggregator Service

On connect, adds socket to group based on page path.

Exposes groups via getter, used by other services.

... also sends chat status events on connect, disconnect

Page Event Listener

Sling   EventHandler   registered for   PageEvents

On modification events, sends list of changed paths to sockets connecting from that page.

Aggregates modification paths since one component edit can yield many changed properties.

RTCA Front-end

Refresh component by path


var ed = CQ.WCM.getEditable(path);
if (!ed) { return; }
ed.refresh();

// Flash updated component
ed.getEl()
    .highlight("ffccaa", {duration: 0.1})
    .pause(0.1)
    .highlight("ffccaa", {duration: 0.1})
    .pause(0.1)
    .highlight("ffccaa", {duration: 0.1});
					

Production Ready?

  • Clustering
  • Logging - won't show up in request.log
  • Modification aggregation for component sub-nodes
    /content/geometrixx/en/jcr:content/par/a/b/c/d
  • In-place editing / dialog-already-open

Additional Considerations

  • Proxy ws connections through Dispatcher
  • Better configuration (no hard-coded ports)
  • Auto-reconnect on connection drop
  • SSL/wss support (easy with Netty)

Future Extensions?

Send update notification from browser making update

  • better info about which components have changed
  • don't need to filter out own changes in front-end

Broadcast Alert To All Users

“System going down for emergency maintenance”

AEM 6 Touch UI

Dynamic Handler Service Registration

  • OSGi Service Tracker
  • Add handler to DispatcherHandler on bundle activation
  • Clean up and remove handler on deactivation

Who Is Editing This Component

Need a "start editing" event to hook

  • Dialog before-open?
  • Editable JS open dialog?
  • in-place editing start function?

Workflow Participant Step

Ping participant directly if they're online

Home-brewed analytics

  • Dashboard showing real-time page edits?
  • How many people are using the system right now?

Questions?

Slides available @http://mszu.github.io/rtca-aem

Code (will be) available on GitHub