All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.coweb.OperationEngineHandler Maven / Gradle / Ivy

There is a newer version: 1.0
Show newest version
package org.coweb;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;

import org.coweb.oe.ContextVector;
import org.coweb.oe.Operation;
import org.coweb.oe.OperationEngine;
import org.coweb.oe.OperationEngineException;
import org.eclipse.jetty.util.ajax.JSON;

public class OperationEngineHandler {
	
	private static final Logger log = Logger.getLogger(OperationEngineHandler.class.getName());

	// reference to operation engine.
	private OperationEngine engine = null;
	
	// reference to session handler.
	private SessionHandler sessionHandler = null;

	// should purge if we've received a sync
	private boolean shouldPurge = false;

	// should sync if we've received a sync and have been quiet
	private boolean shouldSync = false;
	
	private Timer purgeTimer = null;
	private Timer syncTimer = null;

	// Keep these around incase we need to stop the events from firing.
	private PurgeTask purgeTask = null;
	private SyncTask syncTask = null;

	public OperationEngineHandler(SessionHandler sessionHandler, int siteId) throws OperationEngineException {
		
		this.sessionHandler = sessionHandler;
		
		//create the op engine.
		this.engine = new OperationEngine(siteId);
		this.engine.freezeSite(0);
		
		//schedule the purge thread.
		this.purgeTimer = new Timer();
		this.purgeTask = new PurgeTask();
		this.purgeTimer.scheduleAtFixedRate(this.purgeTask, new Date(), 10000);
		
		//schedule the engine sync thread.
		this.syncTimer = new Timer();
		this.syncTask = new SyncTask();
		this.syncTimer.scheduleAtFixedRate(this.syncTask, new Date(), 10000);
	}

	/**
     * Called by the session when a coweb event is received from a remote app.
     * Processes the data in the local operation engine if required before 
     * publishing to the moderator. 
     *
     * @param Map containing the following.
     *        String topic Topic name (topics.SYNC.**)
     *        String value JSON-encoded operation value
     *        String|null type Operation type
     *        Integer position Operation linear position
     *        Integer site Unique integer ID of the sending site
     *        Integer[] sites Context vector as an array of integers (use {@link OperationEngineHandler#getSites} to convert from Integer[] to int[])
     */
	public Map syncInbound(Map data) {
			
		//get the topic
		String topic = (String) data.get("topic");
		
		//get the value
		String value = null;
		if(data.get("value") instanceof String) 
			value = (String)data.get("value");
		else {
			@SuppressWarnings("unchecked")
			Map val = (Map) data.get("value");
			if (val != null) {
				value = JSON.toString(val);
			}
		}
		

		//get the type
		String type = (String) data.get("type");

		//get the position
		Number pos = (Number) data.get("position");
		int position = 0;
		if (pos != null)
			position = pos.intValue();

		//get the site
		Number ste = (Number) data.get("siteId");
		int site = 0;
		if (ste != null) {
			site = ste.intValue();
		}

		//get the sites array
		int[] sites = this.getSites(data);

		//get the order
		Number ord = (Number) data.get("order");
		int order = 0;
		if (ord != null)
			order = ord.intValue();

		//push the operation onto the op engine.
		Operation op = null;
		if (sites != null && type != null) {
			try {
				op = this.engine.push(false, topic, value, type, position,
						site, sites, order);
			} catch (OperationEngineException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return null;
			}

			if (op == null)
				return null;

			value = op.getValue();
			position = op.getPosition();
		} else if (site == this.engine.getSiteId()) {
			// op was echo'ed from server for op engine, but type null means
			// op engine doesn't care about this message anyway so drop it
			return null;
		}
		
		log.info("after engine push");
		log.info(this.engine.toString());

		// value is always json-encoded to avoid ref sharing problems with ops
		// stored inside the op engine history buffer, so decode it and
		// pack it into a hub event
		HashMap hashMap = new HashMap();
		hashMap.put("position", new Integer(position));
		hashMap.put("type", type);
		hashMap.put("value", JSON.parse(value));
		hashMap.put("site", site);
		
		this.shouldPurge = true;
		this.shouldSync = true;

		return hashMap;
	}
	
	/**
     * Called when the listener receives a context vector from a remote op
     * engine (topics.ENGINE_SYNC). Integrates the context vector into context
     * vector table of the local engine. Sets a flag saying the local op engine
     * should run garbage collection over its history. 
     * 
     * @param Map containing the following.
     *        Integer site Unique integer ID of the sending site
     *        int[] sites Context vector as an array of integers
     */
	public void engineSyncInbound(Map data) {
		int[] sites = this.getSites(data);
		
		Integer ste = (Integer) data.get("siteId");
		int site = -1;
		if (ste != null) {
			site = ste.intValue();
		}
		
		// ignore our own engine syncs
        if(site == this.engine.getSiteId()) {
        	return;
        }
        
        // give the engine the data
        try {
            this.engine.pushSyncWithSites(site, sites);
        } catch(OperationEngineException e) {
            log.info("UnmanagedHubListener: failed to recv engine sync " + 
                site + " " + sites + " " + e.getMessage());
        }
        // we've received remote info, allow purge
        this.shouldPurge = true;
	}
	
	private int[] getSites(Map data) {
		int[] sites = null;
		Object[] objArr = (Object[])data.get("context");
		if(objArr != null) {
			sites = new int[objArr.length];
			for(int i=0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy