Package redis.clients.johm

Source Code of redis.clients.johm.JOhm

package redis.clients.johm;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import redis.clients.jedis.JedisException;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.TransactionBlock;
import redis.clients.johm.collections.RedisArray;

/**
* JOhm serves as the delegate responsible for heavy-lifting all mapping
* operations between the Object Models at one end and Redis Persistence Models
* on the other.
*/
public final class JOhm {
    private static JedisPool jedisPool;

    /**
     * Read the id from the given model. This operation will typically be useful
     * only after an initial interaction with Redis in the form of a call to
     * save().
     */
    public static Long getId(final Object model) {
        return JOhmUtils.getId(model);
    }

    /**
     * Check if given model is in the new state with an uninitialized id.
     */
    public static boolean isNew(final Object model) {
        return JOhmUtils.isNew(model);
    }

    /**
     * Load the model persisted in Redis looking it up by its id and Class type.
     *
     * @param <T>
     * @param clazz
     * @param id
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T get(Class<?> clazz, long id) {
        JOhmUtils.Validator.checkValidModelClazz(clazz);

        Nest nest = new Nest(clazz);
        nest.setJedisPool(jedisPool);
        if (!nest.cat(id).exists()) {
            return null;
        }

        Object newInstance;
        try {
            newInstance = clazz.newInstance();
            JOhmUtils.loadId(newInstance, id);
            JOhmUtils.initCollections(newInstance, nest);

            Map<String, String> hashedObject = nest.cat(id).hgetAll();
            for (Field field : JOhmUtils.gatherAllFields(clazz)) {
                fillField(hashedObject, newInstance, field);
                fillArrayField(nest, newInstance, field);
            }

            return (T) newInstance;
        } catch (InstantiationException e) {
            throw new JOhmException(e);
        } catch (IllegalAccessException e) {
            throw new JOhmException(e);
        }
    }

    /**
     * Search a Model in redis index using its attribute's given name/value
     * pair. This can potentially return more than 1 matches if some indexed
     * Model's have identical attributeValue for given attributeName.
     *
     * @param clazz
     *            Class of Model annotated-type to search
     * @param attributeName
     *            Name of Model's attribute to search
     * @param attributeValue
     *            Attribute's value to search in index
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> find(Class<?> clazz, String attributeName,
            Object attributeValue) {
        JOhmUtils.Validator.checkValidModelClazz(clazz);
        List<Object> results = null;
        if (!JOhmUtils.Validator.isIndexable(attributeName)) {
            throw new InvalidFieldException();
        }

        try {
            Field field = clazz.getDeclaredField(attributeName);
            field.setAccessible(true);
            if (!field.isAnnotationPresent(Indexed.class)) {
                throw new InvalidFieldException();
            }
            if (field.isAnnotationPresent(Reference.class)) {
                attributeName = JOhmUtils.getReferenceKeyName(field);
            }
        } catch (SecurityException e) {
            throw new InvalidFieldException();
        } catch (NoSuchFieldException e) {
            throw new InvalidFieldException();
        }
        if (JOhmUtils.isNullOrEmpty(attributeValue)) {
            throw new InvalidFieldException();
        }
        Nest nest = new Nest(clazz);
        nest.setJedisPool(jedisPool);
        Set<String> modelIdStrings = nest.cat(attributeName)
                .cat(attributeValue).smembers();
        if (modelIdStrings != null) {
            // TODO: Do this lazy
            results = new ArrayList<Object>();
            Object indexed = null;
            for (String modelIdString : modelIdStrings) {
                indexed = get(clazz, Long.parseLong(modelIdString));
                if (indexed != null) {
                    results.add(indexed);
                }
            }
        }
        return (List<T>) results;
    }

    /**
     * Save given model to Redis. By default, this does not save all its child
     * annotated-models. If hierarchical persistence is desirable, use the
     * overloaded save interface.
     *
     * @param <T>
     * @param model
     * @return
     */
    public static <T> T save(final Object model) {
        return JOhm.<T> save(model, false);
    }

    @SuppressWarnings("unchecked")
    public static <T> T save(final Object model, boolean saveChildren) {
        if (!isNew(model)) {
            delete(model.getClass(), JOhmUtils.getId(model));
        }
        final Nest nest = initIfNeeded(model);

        final Map<String, String> hashedObject = new HashMap<String, String>();
        Map<RedisArray<Object>, Object[]> pendingArraysToPersist = null;
        try {
            String fieldName = null;
            for (Field field : JOhmUtils.gatherAllFields(model.getClass())) {
                field.setAccessible(true);
                if (JOhmUtils.detectJOhmCollection(field)
                        || field.isAnnotationPresent(Id.class)) {
                    if (field.isAnnotationPresent(Id.class)) {
                        JOhmUtils.Validator.checkValidIdType(field);
                    }
                    continue;
                }
                if (field.isAnnotationPresent(Array.class)) {
                    Object[] backingArray = (Object[]) field.get(model);
                    int actualLength = backingArray == null ? 0
                            : backingArray.length;
                    JOhmUtils.Validator.checkValidArrayBounds(field,
                            actualLength);
                    Array annotation = field.getAnnotation(Array.class);
                    RedisArray<Object> redisArray = new RedisArray<Object>(
                            annotation.length(), annotation.of(), nest, field,
                            model);
                    if (pendingArraysToPersist == null) {
                        pendingArraysToPersist = new LinkedHashMap<RedisArray<Object>, Object[]>();
                    }
                    pendingArraysToPersist.put(redisArray, backingArray);
                }
                JOhmUtils.Validator.checkAttributeReferenceIndexRules(field);
                if (field.isAnnotationPresent(Attribute.class)) {
                    fieldName = field.getName();
                    Object fieldValueObject = field.get(model);
                    if (fieldValueObject != null) {
                        hashedObject
                                .put(fieldName, fieldValueObject.toString());
                    }

                }
                if (field.isAnnotationPresent(Reference.class)) {
                    fieldName = JOhmUtils.getReferenceKeyName(field);
                    Object child = field.get(model);
                    if (child != null) {
                        if (JOhmUtils.getId(child) == null) {
                            throw new MissingIdException();
                        }
                        if (saveChildren) {
                            save(child, saveChildren); // some more work to do
                        }
                        hashedObject.put(fieldName, String.valueOf(JOhmUtils
                                .getId(child)));
                    }
                }
                if (field.isAnnotationPresent(Indexed.class)) {
                    Object fieldValue = field.get(model);
                    if (fieldValue != null
                            && field.isAnnotationPresent(Reference.class)) {
                        fieldValue = JOhmUtils.getId(fieldValue);
                    }
                    if (!JOhmUtils.isNullOrEmpty(fieldValue)) {
                        nest.cat(fieldName).cat(fieldValue).sadd(
                                String.valueOf(JOhmUtils.getId(model)));
                    }
                }
                // always add to the all set, to support getAll
                nest.cat("all").sadd(String.valueOf(JOhmUtils.getId(model)));
            }
        } catch (IllegalArgumentException e) {
            throw new JOhmException(e);
        } catch (IllegalAccessException e) {
            throw new JOhmException(e);
        }

        nest.multi(new TransactionBlock() {
            public void execute() throws JedisException {
                del(nest.cat(JOhmUtils.getId(model)).key());
                hmset(nest.cat(JOhmUtils.getId(model)).key(), hashedObject);
            }
        });

        if (pendingArraysToPersist != null && pendingArraysToPersist.size() > 0) {
            for (Map.Entry<RedisArray<Object>, Object[]> arrayEntry : pendingArraysToPersist
                    .entrySet()) {
                arrayEntry.getKey().write(arrayEntry.getValue());
            }
        }

        return (T) model;
    }

    /**
     * Delete Redis-persisted model as represented by the given model Class type
     * and id.
     *
     * @param clazz
     * @param id
     * @return
     */
    public static boolean delete(Class<?> clazz, long id) {
        return delete(clazz, id, true, false);
    }

    @SuppressWarnings("unchecked")
    public static boolean delete(Class<?> clazz, long id,
            boolean deleteIndexes, boolean deleteChildren) {
        JOhmUtils.Validator.checkValidModelClazz(clazz);
        boolean deleted = false;
        Object persistedModel = get(clazz, id);
        if (persistedModel != null) {
            Nest nest = new Nest(persistedModel);
            nest.setJedisPool(jedisPool);
            if (deleteIndexes) {
                // think about promoting deleteChildren as default behavior so
                // that this field lookup gets folded into that
                // if-deleteChildren block
                for (Field field : JOhmUtils.gatherAllFields(clazz)) {
                    if (field.isAnnotationPresent(Indexed.class)) {
                        field.setAccessible(true);
                        Object fieldValue = null;
                        try {
                            fieldValue = field.get(persistedModel);
                        } catch (IllegalArgumentException e) {
                            throw new JOhmException(e);
                        } catch (IllegalAccessException e) {
                            throw new JOhmException(e);
                        }
                        if (fieldValue != null
                                && field.isAnnotationPresent(Reference.class)) {
                            fieldValue = JOhmUtils.getId(fieldValue);
                        }
                        if (!JOhmUtils.isNullOrEmpty(fieldValue)) {
                            nest.cat(field.getName()).cat(fieldValue).srem(
                                    String.valueOf(id));
                        }
                    }
                }
            }
            if (deleteChildren) {
                for (Field field : JOhmUtils.gatherAllFields(clazz)) {
                    if (field.isAnnotationPresent(Reference.class)) {
                        field.setAccessible(true);
                        try {
                            Object child = field.get(persistedModel);
                            if (child != null) {
                                delete(child.getClass(),
                                        JOhmUtils.getId(child), deleteIndexes,
                                        deleteChildren); // children
                            }
                        } catch (IllegalArgumentException e) {
                            throw new JOhmException(e);
                        } catch (IllegalAccessException e) {
                            throw new JOhmException(e);
                        }
                    }
                    if (field.isAnnotationPresent(Array.class)) {
                        field.setAccessible(true);
                        Array annotation = field.getAnnotation(Array.class);
                        RedisArray redisArray = new RedisArray(annotation
                                .length(), annotation.of(), nest, field,
                                persistedModel);
                        redisArray.clear();
                    }
                }
            }

            // now delete parent
            deleted = nest.cat(id).del() == 1;
        }
        return deleted;
    }

    /**
     * Inject JedisPool into JOhm. This is a mandatory JOhm setup operation.
     *
     * @param jedisPool
     */
    public static void setPool(final JedisPool jedisPool) {
        JOhm.jedisPool = jedisPool;
    }

    private static void fillField(final Map<String, String> hashedObject,
            final Object newInstance, final Field field)
            throws IllegalAccessException {
        JOhmUtils.Validator.checkAttributeReferenceIndexRules(field);
        if (field.isAnnotationPresent(Attribute.class)) {
            field.setAccessible(true);
            field.set(newInstance, JOhmUtils.Convertor.convert(field,
                    hashedObject.get(field.getName())));
        }
        if (field.isAnnotationPresent(Reference.class)) {
            field.setAccessible(true);
            String serializedReferenceId = hashedObject.get(JOhmUtils
                    .getReferenceKeyName(field));
            if (serializedReferenceId != null) {
                Long referenceId = Long.valueOf(serializedReferenceId);
                field.set(newInstance, get(field.getType(), referenceId));
            }
        }
    }

    @SuppressWarnings("unchecked")
    private static void fillArrayField(final Nest nest, final Object model,
            final Field field) throws IllegalArgumentException,
            IllegalAccessException {
        if (field.isAnnotationPresent(Array.class)) {
            field.setAccessible(true);
            Array annotation = field.getAnnotation(Array.class);
            RedisArray redisArray = new RedisArray(annotation.length(),
                    annotation.of(), nest, field, model);
            field.set(model, redisArray.read());
        }
    }

    @SuppressWarnings("unchecked")
    private static Nest initIfNeeded(final Object model) {
        Long id = JOhmUtils.getId(model);
        Nest nest = new Nest(model);
        nest.setJedisPool(jedisPool);
        if (id == null) {
            // lazily initialize id, nest, collections
            id = nest.cat("id").incr();
            JOhmUtils.loadId(model, id);
            JOhmUtils.initCollections(model, nest);
        }
        return nest;
    }

    @SuppressWarnings("unchecked")
    public static <T> Set<T> getAll(Class<?> clazz) {
        JOhmUtils.Validator.checkValidModelClazz(clazz);
        Set<Object> results = null;
        Nest nest = new Nest(clazz);
        nest.setJedisPool(jedisPool);
        Set<String> modelIdStrings = nest.cat("all").smembers();
        if (modelIdStrings != null) {
            results = new HashSet<Object>();
            Object indexed = null;
            for (String modelIdString : modelIdStrings) {
                indexed = get(clazz, Long.parseLong(modelIdString));
                if (indexed != null) {
                    results.add(indexed);
                }
            }
        }
        return (Set<T>) results;
    }
}
TOP

Related Classes of redis.clients.johm.JOhm

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.