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

com.almende.eve.state.couch.CouchState 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.couch;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

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

import com.almende.eve.state.AbstractState;
import com.almende.eve.state.State;
import com.almende.eve.state.couch.CouchStateBuilder.CouchStateProvider;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * The Class CouchState.
 */
public class CouchState extends AbstractState implements State {
	private static final Logger		LOG			= Logger.getLogger(CouchState.class
														.getName());
	private String					revision	= null;
	private Map	properties	= new ConcurrentHashMap();
	private CouchDbConnector		db			= null;
	
	/**
	 * Instantiates a new couch state.
	 */
	public CouchState() {
	}
	
	/**
	 * Instantiates a new couch state.
	 * 
	 * @param id
	 *            the id
	 * @param db
	 *            the db
	 * @param service
	 *            the service
	 * @param params
	 *            the params
	 */
	public CouchState(final String id, final CouchDbConnector db,
			final CouchStateProvider service, final ObjectNode params) {
		super(id, service, params);
		this.db = db;
	}
	
	/**
	 * Read.
	 */
	private void read() {
		try {
			synchronized (properties) {
				final CouchState state = db.get(CouchState.class, getId());
				if (state != null) {
					revision = state.revision;
					properties.clear();
					properties.putAll(state.properties);
				}
			}
		} catch (final org.ektorp.DocumentNotFoundException e) {
		}
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see com.almende.eve.state.AbstractState#locPut(java.lang.String,
	 * com.fasterxml.jackson.databind.JsonNode)
	 */
	@Override
	public JsonNode locPut(final String key, final JsonNode value) {
		final String ckey = couchify(key);
		JsonNode result = null;
		try {
			synchronized (properties) {
				result = properties.put(ckey, value);
			}
			db.update(this);
		} catch (final UpdateConflictException uce) {
			read();
			return 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 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())) {
				synchronized (properties) {
					properties.put(ckey, newVal);
				}
				db.update(this);
				result = true;
			}
		} catch (final UpdateConflictException uce) {
			read();
			return locPutIfUnchanged(ckey, newVal, oldVal);
		} catch (final Exception e) {
			LOG.log(Level.WARNING, "", e);
		}
		
		return result;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see com.almende.eve.state.State#remove(java.lang.String)
	 */
	@Override
	public Object remove(final String key) {
		final String ckey = couchify(key);
		
		Object result = null;
		try {
			synchronized (properties) {
				result = properties.remove(ckey);
			}
			db.update(this);
		} 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);
			if (result == false) {
				read();
				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 void clear() {
		try {
			synchronized (properties) {
				properties.clear();
			}
			db.update(this);
		} 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);
			if (result == null) {
				read();
				result = properties.get(key);
			}
			
		} catch (final Exception e) {
			LOG.log(Level.WARNING, "", e);
		}
		return result;
	}
	
	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	@Override
	@JsonProperty("_id")
	public String getId() {
		return super.getId();
	};
	
	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the new id
	 */
	@Override
	@JsonProperty("_id")
	public void setId(final String id) {
		super.setId(id);
	}
	
	/**
	 * Gets the revision.
	 * 
	 * @return the revision
	 */
	@JsonProperty("_rev")
	@JsonInclude(Include.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;
	}
	
	/**
	 * 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;
	}
	
	/**
	 * 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;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy