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

com.almende.eve.state.couchdb.CouchDBState Maven / Gradle / Ivy

There is a newer version: 3.1.1
Show newest version
/*
 * Copyright: Almende B.V. (2014), Rotterdam, The Netherlands
 * License: The Apache Software License, Version 2.0
 */
package com.almende.eve.state.couchdb;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.ektorp.CouchDbConnector;
import org.ektorp.UpdateConflictException;

import com.almende.eve.rpc.jsonrpc.jackson.JOM;
import com.almende.eve.state.AbstractState;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize.Inclusion;
import com.fasterxml.jackson.databind.node.NullNode;

/**
 * The Class CouchDBState.
 */
public class CouchDBState extends AbstractState {
	private static final Logger		LOG			= Logger.getLogger("CouchDBState");
	private String					revision	= null;
	private Map	properties	= Collections
														.synchronizedMap(new HashMap());
	private CouchDbConnector		db			= null;
	private String					id			= null;
	
	/**
	 * Instantiates a new couch db state.
	 */
	public CouchDBState() {
	}
	
	/**
	 * Instantiates a new couch db state.
	 *
	 * @param agentId the agent id
	 * @param db the db
	 */
	public CouchDBState(final String agentId, final CouchDbConnector db) {
		super(agentId);
		id = couchify(agentId);
		this.db = db;
	}
	
	/**
	 * Read.
	 */
	private void read() {
		final CouchDBState state = db.get(CouchDBState.class, id);
		revision = state.revision;
		properties = state.properties;
	}
	
	/**
	 * Update.
	 */
	private synchronized void update() {
		db.update(this);
	}
	
	/* (non-Javadoc)
	 * @see com.almende.eve.state.AbstractState#locPut(java.lang.String, com.fasterxml.jackson.databind.JsonNode)
	 */
	@Override
	public synchronized JsonNode locPut(final String key, final JsonNode value) {
		final String ckey = couchify(key);
		JsonNode result = null;
		try {
			result = properties.put(ckey, value);
			update();
		} catch (final UpdateConflictException uce) {
			read();
			locPut(ckey, value);
		} catch (final Exception e) {
			LOG.log(Level.WARNING, "Failed to store property", e);
		}
		
		return result;
	}
	
	/* (non-Javadoc)
	 * @see com.almende.eve.state.AbstractState#locPutIfUnchanged(java.lang.String, com.fasterxml.jackson.databind.JsonNode, com.fasterxml.jackson.databind.JsonNode)
	 */
	@Override
	public synchronized boolean locPutIfUnchanged(final String key,
			final JsonNode newVal, JsonNode oldVal) {
		final String ckey = couchify(key);
		boolean result = false;
		try {
			JsonNode cur = NullNode.getInstance();
			if (properties.containsKey(ckey)) {
				cur = properties.get(ckey);
			}
			if (oldVal == null) {
				oldVal = NullNode.getInstance();
			}
			
			// Poor mans equality as some Numbers are compared incorrectly: e.g.
			// IntNode versus LongNode
			if (oldVal.equals(cur) || oldVal.toString().equals(cur.toString())) {
				properties.put(ckey, newVal);
				update();
				result = true;
			}
		} catch (final UpdateConflictException uce) {
			read();
			locPutIfUnchanged(ckey, newVal, oldVal);
		} catch (final Exception e) {
			LOG.log(Level.WARNING, "", e);
		}
		
		return result;
	}
	
	/**
	 * Check the key if it starts with a _
	 * Add a prefix if this is the case, because _ properties are reserved.
	 *
	 * @param key the key
	 * @return prefixed key (if necessary)
	 */
	private String couchify(final String key) {
		if (key.startsWith("_")) {
			return "cdb" + key;
		}
		
		return key;
	}
	
	/**
	 * Check the key if it starts with a _
	 * Add a prefix if this is the case, because _ properties are reserved.
	 *
	 * @param key the key
	 * @return prefixed key (if necessary)
	 */
	private String decouchify(final String key) {
		if (key.startsWith("cdb_")) {
			return key.replaceFirst("cdb_", "_");
		}
		
		return key;
	}
	
	/* (non-Javadoc)
	 * @see com.almende.eve.state.State#init()
	 */
	@Override
	public void init() {
	}
	
	/* (non-Javadoc)
	 * @see com.almende.eve.state.State#destroy()
	 */
	@Override
	public void destroy() {
	}
	
	/* (non-Javadoc)
	 * @see com.almende.eve.state.State#remove(java.lang.String)
	 */
	@Override
	public synchronized Object remove(final String key) {
		Object result = null;
		try {
			result = properties.remove(key);
			update();
		} catch (final Exception e) {
			LOG.log(Level.WARNING, "", e);
		}
		return result;
	}
	
	/* (non-Javadoc)
	 * @see com.almende.eve.state.State#containsKey(java.lang.String)
	 */
	@Override
	public boolean containsKey(final String key) {
		final String ckey = couchify(key);
		boolean result = false;
		try {
			result = properties.containsKey(ckey);
		} catch (final Exception e) {
			LOG.log(Level.WARNING, "", e);
		}
		return result;
	}
	
	/* (non-Javadoc)
	 * @see com.almende.eve.state.State#keySet()
	 */
	@Override
	public Set keySet() {
		final Set result = new HashSet();
		Set keys = null;
		try {
			keys = new HashSet(properties.keySet());
			for (final String key : keys) {
				result.add(decouchify(key));
			}
		} catch (final Exception e) {
			LOG.log(Level.WARNING, "", e);
		}
		return result;
	}
	
	/* (non-Javadoc)
	 * @see com.almende.eve.state.State#clear()
	 */
	@Override
	public synchronized void clear() {
		try {
			final String agentType = properties
					.get(couchify(KEY_AGENT_TYPE)).textValue();
			properties.clear();
			properties.put(couchify(KEY_AGENT_TYPE), JOM.getInstance()
					.valueToTree(agentType));
			update();
		} catch (final Exception e) {
			LOG.log(Level.WARNING, "Failed clearing state", e);
		}
	}
	
	/* (non-Javadoc)
	 * @see com.almende.eve.state.State#size()
	 */
	@Override
	public int size() {
		int result = -1;
		try {
			result = properties.size();
		} catch (final Exception e) {
			LOG.log(Level.WARNING, "", e);
		}
		return result;
	}
	
	/* (non-Javadoc)
	 * @see com.almende.eve.state.AbstractState#get(java.lang.String)
	 */
	@Override
	public JsonNode get(String key) {
		key = couchify(key);
		JsonNode result = null;
		try {
			result = properties.get(key);
		} catch (final Exception e) {
			LOG.log(Level.WARNING, "", e);
		}
		return result;
	}
	
	/**
	 * Gets the id.
	 *
	 * @return the id
	 */
	@JsonProperty("_id")
	public String getId() {
		return id;
	};
	
	/**
	 * Sets the id.
	 *
	 * @param id the new id
	 */
	@JsonProperty("_id")
	public void setId(final String id) {
		this.id = id;
	}
	
	/**
	 * Gets the revision.
	 *
	 * @return the revision
	 */
	@JsonProperty("_rev")
	@JsonSerialize(include = Inclusion.NON_NULL)
	public String getRevision() {
		return revision;
	}
	
	/**
	 * Sets the revision.
	 *
	 * @param revision the new revision
	 */
	@JsonProperty("_rev")
	public void setRevision(final String revision) {
		this.revision = revision;
	}
	
	/* (non-Javadoc)
	 * @see com.almende.eve.state.AbstractState#getAgentType()
	 */
	@Override
	@JsonIgnore
	public synchronized Class getAgentType() throws ClassNotFoundException {
		return super.getAgentType();
	}
	
	/* (non-Javadoc)
	 * @see com.almende.eve.state.AbstractState#setAgentType(java.lang.Class)
	 */
	@Override
	@JsonIgnore
	public synchronized void setAgentType(final Class agentType) {
		super.setAgentType(agentType);
	}
	
	/**
	 * Gets the properties.
	 *
	 * @return the properties
	 */
	public Map getProperties() {
		return properties;
	}
	
	/**
	 * Sets the properties.
	 *
	 * @param properties the properties
	 */
	public void setProperties(final Map properties) {
		this.properties = properties;
	}
	
	/**
	 * Sets the db.
	 *
	 * @param db the new db
	 */
	public void setDb(final CouchDbConnector db) {
		this.db = db;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy