Package com.mgreau.wwsmad.websocket

Source Code of com.mgreau.wwsmad.websocket.WWSMADEndpoint

package com.mgreau.wwsmad.websocket;

import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.asciidoctor.Asciidoctor;

import com.mgreau.wwsmad.cdi.AsciidocMessageEvent;
import com.mgreau.wwsmad.cdi.qualifier.Backend;
import com.mgreau.wwsmad.cdi.qualifier.ComputeDiff;
import com.mgreau.wwsmad.cdi.qualifier.Patch;
import com.mgreau.wwsmad.websocket.decoders.MessageDecoder;
import com.mgreau.wwsmad.websocket.encoders.AsciidocMessageEncoder;
import com.mgreau.wwsmad.websocket.encoders.NotificationMessageEncoder;
import com.mgreau.wwsmad.websocket.encoders.OutputMessageEncoder;
import com.mgreau.wwsmad.websocket.messages.AsciidocMessage;
import com.mgreau.wwsmad.websocket.messages.Message;
import com.mgreau.wwsmad.websocket.messages.NotificationMessage;
import com.mgreau.wwsmad.websocket.messages.TypeMessage;

/**
* WSMAD : When WebSocket met Asciidoctor !
*
* @author greau.maxime@gmail.com
*
*/
@ServerEndpoint(value = "/adoc/{adoc-id}",
        decoders = { MessageDecoder.class },
        encoders = { AsciidocMessageEncoder.class, OutputMessageEncoder.class,
              NotificationMessageEncoder.class })
public class WWSMADEndpoint {

  private static final Logger logger = Logger.getLogger("WSMADEndpoint");

  /** All open WebSocket sessions */
  static Set<Session> peers = Collections
      .synchronizedSet(new HashSet<Session>());

  /** Handle number of readers (people who don't send asciidoc source) by adoc file */
  static Map<String, AtomicInteger> nbReadersByAdoc = new ConcurrentHashMap<>();

  /** Handle number of writers (people who send at least one asciidoc source)  by adoc file */
  static Map<String, Set<String>> writersByAdoc = new ConcurrentHashMap<String, Set<String>>();
 
  @Inject @Patch
  Event<AsciidocMessageEvent> patchEvent;
 
  @Inject @ComputeDiff
  Event<AsciidocMessageEvent> diffEvent;
 
  @Inject @Backend("html5")
  Event<AsciidocMessageEvent> html5Event;
 
  @Inject @Backend("dzslides")
  Event<AsciidocMessageEvent> dzEvent;
 
  @Inject @Backend("pdf")
  Event<AsciidocMessageEvent> pdfEvent;

  /**
   * Send, to all peers connected to this asciidoc file, the Output Message
   * resulted of an asciidoc source processor.
   *
   * @param msg
   *            OutputMessage
   * @param adocId
   *            unique id for this asciidoc file
   */
  public static void sendMessage(Message msg, String adocId) {
    NotificationMessage nfMsg = new NotificationMessage();
    nfMsg.setNbConnected(nbReadersByAdoc.get(adocId).get());
    nfMsg.setAdocId(adocId);
    nfMsg.setType(TypeMessage.notification);

    //try {
      for (Session session : peers) {
        if (Boolean.TRUE
            .equals(session.getUserProperties().get(adocId))) {
          if (session.isOpen()) {
            session.getAsyncRemote().sendObject(msg);
            logger.log(Level.INFO, " Outpout Sent : ",
                msg.toString());
            sendNotificationMessage(session, nfMsg, adocId);
          }
        }
      }
    //} catch (IOException | EncodeException e) {
      //logger.log(Level.SEVERE, e.getCause().toString());
    //}
  }
 
  /**
   * Send, to one peer
   *
   * @param msg
   *            OutputMessage
   * @param adocId
   *            unique id for this asciidoc file
   */
  public static void sendMessage(Session session, Message msg, String adocId) {
    NotificationMessage nfMsg = new NotificationMessage();
    nfMsg.setNbConnected(nbReadersByAdoc.get(adocId).get());
    nfMsg.setAdocId(adocId);
    nfMsg.setType(TypeMessage.notification);

    //try {
      if (Boolean.TRUE
          .equals(session.getUserProperties().get(adocId))) {
        if (session.isOpen()) {
          session.getAsyncRemote().sendObject(msg);
          logger.log(Level.INFO, " Outpout Sent : ",
              msg.toString());
          sendNotificationMessage(session, nfMsg, adocId);
        }
      }
    //} catch (IOException | EncodeException e) {
      //logger.log(Level.SEVERE, e.getCause().toString());
    //}
  }

  /**
   * Send a notification to one peer define by the session
   *
   * @param session
   * @param msg
   * @param adocId
   */
  public static void sendNotificationMessage(Session session,
      NotificationMessage msg, String adocId) {
    try {
      msg.getWriters().addAll(writersByAdoc.get(adocId));
      session.getAsyncRemote().sendObject(msg);
      logger.log(Level.INFO, "Notification Sent: {0}", msg.toString());
    } catch (Exception e) {
      logger.log(Level.SEVERE, e.toString());
    }
  }

  /**
   * Send notifications to all connected peers.
   *
   * @param msg
   * @param adocId
   */
  public static void sendNotificationMessage(NotificationMessage msg,
      String adocId) {
    for (Session session : peers) {
      if (Boolean.TRUE.equals(session.getUserProperties().get(adocId))) {
        if (session.isOpen()) {
          sendNotificationMessage(session, msg, adocId);
        }
      }
    }
  }

  /**
   * Received a AsciidocMessage from one peer with a new version of the source
   * file.
   *
   * @param session
   *            peer who send a new version
   * @param msg
   *            the AsciidocMessage
   * @param adocId
   *            unique id for this asciidoc file
   */
  @OnMessage
  public void message(final Session session, AsciidocMessage msg,
      @PathParam("adoc-id") String adocId) {
    logger.log(Level.INFO, "Received: Asciidoc source from - {0}", msg);

    // check if the user had already send a version for this doc
    boolean wasAlreadyAnAuhtor = session.getUserProperties().containsKey(
        "writer");
    if (!wasAlreadyAnAuhtor) {
      session.getUserProperties().put("writer", msg.getCurrentWriter());
      handleReaders(adocId, false);
      handleWriters(adocId, true, msg.getCurrentWriter());
    } else {
      // TODO handle if the name is modified
    }
   
    final AsciidocMessageEvent event = new AsciidocMessageEvent(session, adocId, msg);
    if (null == msg.getAction())
      html5Event.fire(event);
    else
    switch (msg.getAction()) {
      case "diff":
        diffEvent.fire(event);
        break;
 
      case "patch":
        patchEvent.fire(event);
        break;
 
      case "backendHtml5":
        html5Event.fire(event);
        break;
       
      case "backendDzSlides":
        dzEvent.fire(event);
        break;
       
      case "backendPdf":
        pdfEvent.fire(event);
        break;
 
      default:
        html5Event.fire(event);
        break;
    }
  }

  @OnOpen
  public void openConnection(Session session,
      @PathParam("adoc-id") String adocId) {
    logger.log(Level.INFO, "Session ID : " + session.getId()
        + " - Connection opened for doc : " + adocId);
    session.getUserProperties().put(adocId, true);
    peers.add(session);
    // send a message to all peers to inform that someone is connected
    handleReaders(adocId, true);
    if (!writersByAdoc.containsKey(adocId)) {
      writersByAdoc.put(adocId, new HashSet<String>());
    }

    sendNotificationMessage(createNotification(adocId), adocId);
  }

  /**
   * Close the connection and decrement the number of writers and send a
   * message to notify all others writers.
   *
   * @param session
   *            peer session
   * @param adocId
   *            unique id for this asciidoc file
   */
  @OnClose
  public void closedConnection(Session session,
      @PathParam("adoc-id") String adocId) {
    if (session.getUserProperties().containsKey("writer")) {
      handleWriters(adocId, false, (String) session.getUserProperties()
          .get("writer"));
    } else {
      handleReaders(adocId, false);
    }

    peers.remove(session);
    logger.log(Level.INFO, "Connection closed for " + session.getId());
    // send a message to all peers to inform that someone is disonnected
    sendNotificationMessage(createNotification(adocId), adocId);
  }

  @OnError
  public void error(Session session, Throwable t) {
    // TODO send a notification to alert user
    logger.log(Level.SEVERE, t.toString());
    logger.log(Level.SEVERE, "Connection error!");
  }

  private void handleReaders(String adocId, boolean isPlus) {
    // check if there is any author for this id
    if (!nbReadersByAdoc.containsKey(adocId)) {
      nbReadersByAdoc.put(adocId, new AtomicInteger());
    }
    if (isPlus)
      nbReadersByAdoc.get(adocId).incrementAndGet();
    else
      nbReadersByAdoc.get(adocId).decrementAndGet();
  }

  private void handleWriters(String adocId, boolean isPlus, String writer) {
    if (!writersByAdoc.containsKey(adocId)) {
      writersByAdoc.put(adocId, new HashSet<String>());
    }
    if (isPlus)
      writersByAdoc.get(adocId).add(writer);
    else
      writersByAdoc.get(adocId).remove(writer);
  }

  private NotificationMessage createNotification(String adocId) {
    NotificationMessage nfMsg = new NotificationMessage();
    nfMsg.setNbConnected(nbReadersByAdoc.get(adocId).get());
    nfMsg.setAdocId(adocId);
    nfMsg.setType(TypeMessage.notification);
    return nfMsg;
  }


}
TOP

Related Classes of com.mgreau.wwsmad.websocket.WWSMADEndpoint

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.