com.almende.eve.state.couch.CouchState.java Source code

Java tutorial

Introduction

Here is the source code for com.almende.eve.state.couch.CouchState.java

Source

/*
 * 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<JsonNode> implements State {
    private static final Logger LOG = Logger.getLogger(CouchState.class.getName());
    private String revision = null;
    private Map<String, JsonNode> properties = new ConcurrentHashMap<String, JsonNode>();
    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<String> keySet() {
        final Set<String> result = new HashSet<String>();
        Set<String> keys = null;
        try {
            keys = new HashSet<String>(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<String, JsonNode> getProperties() {
        return properties;
    }

    /**
     * Sets the properties.
     * 
     * @param properties
     *            the properties
     */
    public void setProperties(final Map<String, JsonNode> 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;
    }
}