ch.algotrader.cache.EntityHandler.java Source code

Java tutorial

Introduction

Here is the source code for ch.algotrader.cache.EntityHandler.java

Source

/***********************************************************************************
 * AlgoTrader Enterprise Trading Framework
 *
 * Copyright (C) 2015 AlgoTrader GmbH - All rights reserved
 *
 * All information contained herein is, and remains the property of AlgoTrader GmbH.
 * The intellectual and technical concepts contained herein are proprietary to
 * AlgoTrader GmbH. Modification, translation, reverse engineering, decompilation,
 * disassembly or reproduction of this material is strictly forbidden unless prior
 * written permission is obtained from AlgoTrader GmbH
 *
 * Fur detailed terms and conditions consult the file LICENSE.txt or contact
 *
 * AlgoTrader GmbH
 * Aeschstrasse 6
 * 8834 Schindellegi
 ***********************************************************************************/
package ch.algotrader.cache;

import java.lang.reflect.Field;
import java.util.List;

import org.apache.commons.lang.Validate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;

import ch.algotrader.cache.CacheResponse.CacheState;
import ch.algotrader.entity.BaseEntityI;
import ch.algotrader.util.FieldUtil;

/**
 * Cache Handler for Entities.
 *
 * @author <a href="mailto:aflury@algotrader.ch">Andy Flury</a>
 */
public class EntityHandler extends AbstractHandler {

    private static final Logger LOGGER = LogManager.getLogger(EntityHandler.class);

    private final CacheManagerImpl cacheManager;

    public EntityHandler(CacheManagerImpl cacheManager) {
        this.cacheManager = cacheManager;
    }

    @Override
    protected CacheResponse put(Object obj, List<EntityCacheSubKey> stack) {

        // do not process HibernateProxies
        if (obj instanceof HibernateProxy) {
            return CacheResponse.skippedObject();
        }

        // check stack
        BaseEntityI entity = (BaseEntityI) obj;
        EntityCacheKey rootCacheKey = new EntityCacheKey(entity);
        EntityCacheSubKey subCacheKey = new EntityCacheSubKey(entity);
        if (stack.contains(subCacheKey)) {
            return CacheResponse.processedObject();
        } else {
            stack.add(subCacheKey);
        }

        synchronized (obj) {

            // check if the object is already exists in the cache
            if (this.cacheManager.getEntityCache().exists(rootCacheKey, EntityCacheKey.ROOT)) {

                Object existingObj = this.cacheManager.getEntityCache().find(rootCacheKey, EntityCacheKey.ROOT);
                if (obj != existingObj) {
                    processFieldsWithExisting(entity.getId(), obj, existingObj, stack);
                }
                return CacheResponse.existingObject(existingObj);

            } else {

                this.cacheManager.getEntityCache().attach(rootCacheKey, EntityCacheKey.ROOT, obj);
                processFields(entity.getId(), obj, stack);
                return CacheResponse.newObject();
            }
        }
    }

    private void processFieldsWithExisting(long entityId, Object obj, Object existingObj,
            List<EntityCacheSubKey> stack) {

        Validate.notNull(obj, "obj is null");
        Validate.notNull(existingObj, "existingObj is null");

        for (Field field : FieldUtil.getAllFields(obj.getClass())) {

            Object value = null;
            try {
                value = field.get(obj);
            } catch (Exception e) {
                LOGGER.error("problem getting field", e);
            }

            // nothing to do on simple attributes
            if (FieldUtil.isSimpleAttribute(field) || value == null) {
                continue;
            }

            // if the object already existed but does not have the same reference replace it
            CacheResponse response = this.cacheManager.put(value, stack);

            try {
                if (response.getState() == CacheState.EXISTING) {
                    Object existingValue = field.get(existingObj);
                    if (response.getValue() != existingValue) {

                        field.set(existingObj, response.getValue());

                        EntityCacheKey cacheKey = new EntityCacheKey(field.getDeclaringClass(), entityId);
                        this.cacheManager.getEntityCache().attach(cacheKey, field.getName(), response.getValue());
                    }
                } else if (response.getState() == CacheState.NEW) {
                    Object existingValue = field.get(existingObj);
                    if (existingValue != value) {

                        field.set(existingObj, value);

                        EntityCacheKey cacheKey = new EntityCacheKey(field.getDeclaringClass(), entityId);
                        this.cacheManager.getEntityCache().attach(cacheKey, field.getName(), value);
                    }
                }
            } catch (IllegalArgumentException e) {
                LOGGER.error("problem update field value", e);
            } catch (IllegalAccessException e) {
                LOGGER.error("problem update field value", e);
            }
        }
    }

    private void processFields(long entityId, Object obj, List<EntityCacheSubKey> stack) {

        Validate.notNull(obj, "obj is null");

        for (Field field : FieldUtil.getAllFields(obj.getClass())) {

            Object value = null;
            try {
                value = field.get(obj);
            } catch (Exception e) {
                LOGGER.error("problem getting field", e);
            }

            // nothing to do on simple attributes
            if (FieldUtil.isSimpleAttribute(field) || value == null) {
                continue;
            }

            // if the object already existed but does not have the same reference replace it
            CacheResponse response = this.cacheManager.put(value, stack);

            try {
                if (response.getState() == CacheState.EXISTING && response.getValue() != value) {
                    field.set(obj, response.getValue());
                }

                EntityCacheKey cacheKey = new EntityCacheKey(field.getDeclaringClass(), entityId);
                this.cacheManager.getEntityCache().attach(cacheKey, field.getName(), value);
            } catch (IllegalArgumentException e) {
                LOGGER.error("problem update field value", e);
            } catch (IllegalAccessException e) {
                LOGGER.error("problem update field value", e);
            }
        }
    }

    @Override
    protected CacheResponse update(Object obj) {

        synchronized (obj) {

            // get the updatedObj
            BaseEntityI origEntity = (BaseEntityI) obj;
            BaseEntityI updatedEntity = this.cacheManager.getGenericDao().get(origEntity.getClass(),
                    origEntity.getId());

            // updatedObj does not exist anymore so remove it from the cache
            if (updatedEntity == null) {

                EntityCacheKey cacheKey = new EntityCacheKey(origEntity);
                this.cacheManager.getEntityCache().detach(cacheKey);

                return CacheResponse.removedObject();

            } else {

                // replace all simple Attributes
                for (Field field : FieldUtil.getAllFields(obj.getClass())) {

                    if (FieldUtil.isSimpleAttribute(field)) {

                        try {
                            Object updatedValue = field.get(updatedEntity);
                            Object origValue = field.get(origEntity);

                            if (updatedValue == null) {
                                if (origValue != null) {
                                    field.set(origEntity, null);
                                }
                            } else {
                                if (!updatedValue.equals(origValue)) {
                                    field.set(origEntity, updatedValue);
                                }
                            }
                        } catch (Exception e) {
                            LOGGER.error("problem accessing field", e);
                        }
                    }
                }

                return CacheResponse.updatedObject(updatedEntity);
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    protected CacheResponse initialize(Object obj) {

        if (!(obj instanceof HibernateProxy)) {
            throw new IllegalArgumentException("none HibernateProxy passed " + obj);
        }

        synchronized (obj) {

            HibernateProxy proxy = (HibernateProxy) obj;
            LazyInitializer initializer = proxy.getHibernateLazyInitializer();
            Object initializedObj = this.cacheManager.getGenericDao().get(initializer.getPersistentClass(),
                    (Long) initializer.getIdentifier());

            CacheResponse response = this.cacheManager.put(initializedObj);

            if (response.getState() == CacheState.EXISTING) {
                return response;
            } else {
                return CacheResponse.updatedObject(initializedObj);
            }
        }
    }

    @Override
    protected boolean handles(Class<?> clazz) {
        return BaseEntityI.class.isAssignableFrom(clazz);
    }
}