Package com.adaptrex.core.persistence.jpa

Source Code of com.adaptrex.core.persistence.jpa.JPAModelInstance

/*
* Copyright 2012 Adaptrex, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.adaptrex.core.persistence.jpa;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.persistence.Cache;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;

import org.apache.log4j.Logger;

import com.adaptrex.core.ext.ExtConfig;
import com.adaptrex.core.persistence.api.ORMModelInstance;
import com.adaptrex.core.persistence.api.ORMPersistenceManager;
import com.adaptrex.core.AdaptrexRegistry;
import com.adaptrex.core.utilities.Inflector;
import com.adaptrex.core.utilities.StringUtilities;

public class JPAModelInstance implements ORMModelInstance {

  private static Logger log = Logger.getLogger(JPAModelInstance.class);
 
  private ORMPersistenceManager orm;
  private Map<String, Object> graph;
  private ExtConfig extConfig;
  private Object id;
  private Object entity;

  private static SimpleDateFormat dateFormat = new SimpleDateFormat(
      "yyyy-MM-dd");
  private static SimpleDateFormat timeFormat = new SimpleDateFormat(
      "HH:mm:ss");

  public JPAModelInstance(ExtConfig extConfig) {
    this.extConfig = extConfig;
  }

  public JPAModelInstance(ExtConfig extConfig, Object entity) {
    this(extConfig, entity, null);
  }

  public JPAModelInstance(ExtConfig extConfig, Object entity, EntityManager db) {
    this.extConfig = extConfig;
    this.entity = entity;

    orm = AdaptrexRegistry.getPersistenceManager(extConfig.getFactoryName());
    try {
      graph = this.getObjectGraph(entity, extConfig.getModelName(), null,
          null);
      this.id = graph.get("id");
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public Map<String, Object> getData() {
    return graph;
  }

  @Override
  public Object getId() {
    return id;
  }

  private Map<String, Object> getObjectGraph(Object entity,
      String entityFieldName, Object parentEntity, String parentName) {
    Map<String, Object> entityData = new HashMap<String, Object>();
    if (entity == null) {
      return entityData;
    }

    Class<?> entityClazz = entity.getClass();
    boolean isRoot = parentName == null;
    String entityName = isRoot ? entityFieldName : parentName
        + StringUtilities.capitalize(entityFieldName);

    List<String> entityIncludes = new ArrayList<String>();
    List<String> entityExcludes = new ArrayList<String>();
    List<String> entityJoins = new ArrayList<String>();

    if (isRoot) {
      /*
       * Loop each include/exclude/join config and determine if they are
       * set for the root entity.
       */
      for (String incl : extConfig.getIncludes()) {
        if (!incl.contains(".")) {
          entityIncludes.add(incl);
        }
      }
      for (String excl : extConfig.getExcludes()) {
        if (!excl.contains(".")) {
          entityExcludes.add(excl);
        }
      }
      for (String join : extConfig.getAssociations()) {
        if (!join.contains(".")) {
          entityJoins.add(join);
        }
      }
    } else {
      /*
       * Loop each include/exclude/join config and determine if they are
       * set for the current entity at this position in the tree. If the
       * full path to a specific field on this entity is set, we want to
       * include it in the entity specific config. We only need the field
       * portion when testing against the current entity so that gets
       * split out before adding to the list
       */
      for (String incl : extConfig.getIncludes()) {
        if (incl.contains(entityName + ".")) {
          entityIncludes.add(incl.split("\\.")[1]);
        }
      }

      for (String excl : extConfig.getExcludes()) {
        if (excl.contains(entityName + ".")) {
          entityExcludes.add(excl.split("\\.")[1]);
        }
      }

      for (String join : extConfig.getAssociations()) {
        if (join.contains(entityName + ".")) {
          entityJoins.add(join = join.split("\\.")[1]);
        }
      }
    }

    /*
     * Loop through fields on our entity and determine if they should be
     * added
     */
    for (Field field : entityClazz.getDeclaredFields()) {
      /*
       * Static fields don't get returned (reveng may add static fields to
       * the class)
       */
      if (Modifier.isStatic(field.getModifiers())) {
        continue;
      }

      /*
       * Get the field name
       */
      String fieldName = field.getName();
     
      /*
       * Confirm we should be including this field for this entity. ID
       * fields get added regardless of whether they are explicitly
       * included.
       */
      if (!doInclude(entityClazz, fieldName, entityIncludes,
          entityExcludes)) {
        continue;
      }

      /*
       * Get the field value and make sure it's not this entities parent
       * (prevent infinite recursion)
       */
      Object fieldValue = orm.getFieldValue(entity, fieldName);

      /*
       * Process one to many
       */
      if (orm.isOneToMany(entityClazz, fieldName)
          || orm.isManyToMany(entityClazz, fieldName)) {
        try {
          String idFieldName = Inflector.getInstance().singularize(
              fieldName)
              + "Ids";
          boolean includeAssociationIds = doInclude(entityClazz,
              idFieldName, entityIncludes, entityExcludes);
          boolean includeAssociation = entityJoins
              .contains(StringUtilities.capitalize(fieldName));
         
          if (!includeAssociationIds && !includeAssociation) {
            continue;
          }

          @SuppressWarnings("unchecked")
          List<Object> assocObjList = new ArrayList<Object>(
              (Collection<? extends Object>) fieldValue);

          List<Object> associatedIds = new ArrayList<Object>();
          List<Map<String, Object>> associatedData = new ArrayList<Map<String, Object>>();
          for (Object assocObj : assocObjList) {

            /*
             * Don't go back up the tree
             */
            if (includeAssociation
                && !assocObjList.contains(parentEntity)) {
              Map<String, Object> associatedGraph = getObjectGraph(
                  assocObj, fieldName, entity, entityName);
              associatedData.add(associatedGraph);
            }

            if (includeAssociationIds) {
              associatedIds.add(orm.getEntityId(assocObj));
            }
          }

          if (includeAssociation
              && !assocObjList.contains(parentEntity)) {
            entityData.put(fieldName, associatedData);
          }
          if (includeAssociationIds) {
            entityData.put(idFieldName, associatedIds);
          }

        } catch (Exception e) {
          // log.warn("Error", e);
        }
        continue;
      }

      /*
       * Process many to one
       */
      if (orm.isManyToOne(entityClazz, fieldName)) {
        try {
          String idFieldName = fieldName + "Id";
          boolean includeAssociationId = doInclude(entityClazz,
              idFieldName, entityIncludes, entityExcludes);
          boolean includeAssociation = entityJoins
              .contains(StringUtilities.capitalize(fieldName));
         
          Map<String, Object> associatedGraph = null;
          if (includeAssociation && !fieldValue.equals(parentEntity)) {
            associatedGraph = getObjectGraph(fieldValue, fieldName,
                entity, entityName);
            entityData.put(fieldName, associatedGraph);
          }
          if (includeAssociationId) {
            Object idValue = orm.getEntityId(fieldValue);
            entityData.put(idFieldName, idValue);
          }
        } catch (Exception e) {
        }

        continue;
      }

      /*
       * Process standard fields
       */
      try {
        String fieldType = orm.getFieldType(entityClazz, fieldName);

        if (fieldType.equals("date")) {
          fieldValue = dateFormat.format(fieldValue);
        } else if (fieldType.equals("time")) {
          fieldValue = timeFormat.format(fieldValue);
        }

        entityData.put(fieldName, fieldValue);

      } catch (Exception e) {
        // log.warn("Error", e);
      }
    }

    return entityData;
  }

 
  private static String[]  dateFormatStrings = {"yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd", "HH:mm:ss"};
  @Override
  public ORMModelInstance update(Map<String, Object> data) {
    ORMPersistenceManager orm = AdaptrexRegistry.getPersistenceManager(this.extConfig.getFactoryName());
    EntityManager em = (EntityManager) orm.getSession();
    EntityTransaction tx = em.getTransaction();
   
    try {
      tx.begin();
      for (String key : data.keySet()) {
        if (key.equals("id")) {
          continue;
        }
        Object val = data.get(key);

        /*
         * Get our class
         */
        Class<?> clazz = this.entity.getClass();
       
        /*
         * Get info about the field we're updating
         */
        Field field = null;
        try {
          field = clazz.getDeclaredField(key);
        } catch (Exception e) {
        }
       
       
        /*
         * If we don't have a field with the current name, check for a
         * foreign entity
         */
        if (key.endsWith("Id")
            && orm.isManyToOne(clazz,
                key.substring(0, key.length() - 2))) {
         
          key = key.substring(0, key.length() - 2);
          try {
            field = entity.getClass().getDeclaredField(key);
            if (field != null && val != null) {
              val = orm.getEntity(field.getType(), val);
            }
          } catch (Exception e) {
            log.debug("Couldn't set " + field.getName());
            continue;
          }
        }

        /*
         * Check for 1:m or n:m
         */
        if (key.endsWith("Ids")) {
          key = StringUtilities.pluralize(key.substring(0, key.length() - 3));
          if (orm.isManyToOne(clazz, key) || orm.isManyToMany(clazz, key)) {
            Class<?> fieldType = null;
           
            try {
              evictAssociated(em, entity);
            } catch (Exception e) {
              log.info("Couldn't evict associated entity for " + entity.getClass().getName());
            }
           
            try {
              field = entity.getClass().getDeclaredField(key);
              fieldType = field.getType();
             
              List<Object> assocObjList = new ArrayList<Object>();

              Class<?> itemType = null;
              Type type = field.getGenericType();
              if (type instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) type;
                itemType = (Class<?>) pt.getActualTypeArguments()[0];
              }
             
              @SuppressWarnings("unchecked")
              List<Object> listItemIds = (List<Object>) StringUtilities.fromJson((String) val, List.class);
              for (Object listItemId : listItemIds) {
                assocObjList.add(orm.getEntity(itemType, listItemId));
              }

              if (fieldType.getSimpleName().equals("Set")) {
                val = new HashSet<Object>(assocObjList);
              } else {
                val = assocObjList;
              }
             
            } catch (Exception e) {
              log.info("Error: " + e.getLocalizedMessage());
              log.info("Couldn't set field: " + "set" + StringUtilities.capitalize(key) +
                  "(" + fieldType.getSimpleName() + ")");
              continue;
            }
          }
        }
       
       
       
        if (field == null) {
          continue;
        }

       
        Class<?> fieldType = field.getType();
        String typeName = fieldType.getSimpleName().toLowerCase();

        /*
         * Handle Date Fields
         */
        if (typeName.equals("date")) {
          for (String formatString : dateFormatStrings) {
            try {
              val = new SimpleDateFormat(formatString, Locale.ENGLISH)
                  .parse((String) val);
              break;
            } catch (Exception e) {
            }
          }
        } else if (typeName.equals("float")) {
          if (val instanceof Integer) {
            val = new Float((Integer) val);
          } else if (val instanceof Double) {
            val = ((Double) val).floatValue();
          }
        }

       
        try {
          Method setter = clazz.getMethod(
              "set" + StringUtilities.capitalize(key),
              fieldType);
          setter.invoke(entity, val);
        } catch (Exception e) {
          log.info("Couldn't set field: " + "set" + StringUtilities.capitalize(key) +
              "(" + field.getType().getSimpleName() + "): Received " +
                val.getClass().getSimpleName());
        }
      }
     
      if (id != null && em.find(entity.getClass(), id) != null) {
        entity = em.merge(entity);
      } else {
        em.persist(entity);
      }
     
      try {
        evictAssociated(em, entity);
      } catch (Exception e) {
        log.info("Couldn't evict associated entity for " + entity.getClass().getName());
      }
     
      tx.commit();
    } catch (Exception e) {
      if (tx.isActive()) {
        tx.rollback();
      }
      throw new RuntimeException (e);
    } finally {
      em.close();
    }

    graph = this.getObjectGraph(entity, extConfig.getModelName(), null,
        null);
    this.id = graph.get("id");
    return this;
  }

  @Override
  public ORMModelInstance delete() {

    ORMPersistenceManager orm = AdaptrexRegistry.getPersistenceManager(this.extConfig.getFactoryName());
    EntityManager em = (EntityManager) orm.getSession();
    EntityTransaction tx = em.getTransaction();
    try {
      tx.begin();
     
      try {
        evictAssociated(em, entity);
      } catch (Exception e) {
        log.info("Couldn't evict associated entity for " + entity.getClass().getName());
      }
     
      em.remove(em.merge(this.entity));
     
      tx.commit();
    } catch (Exception e) {
      if (tx.isActive()) {
        tx.rollback();
      }
      throw new RuntimeException (e);
    } finally {
      em.close();
    }
   
    return this;
  }

  private boolean doInclude(Class<?> entityClazz, String fieldName,
      List<String> entityIncludes, List<String> entityExcludes) {
    if (orm.isIdField(entityClazz, fieldName)) {
      return true;
    }

    if (extConfig.getIncludes().size() > 0) {
      if (entityIncludes.isEmpty()
          || (!entityIncludes.contains("*")
              && !entityIncludes.contains(fieldName)
              && !entityIncludes.contains(fieldName + "Id") && !entityIncludes
                .contains(fieldName + "Ids"))) {

        return false;
      }
    }

    if (extConfig.getExcludes().size() > 0) {
      if (entityExcludes.contains("*")
          || entityExcludes.contains(fieldName)) {
        return false;
      }
    }

    return true;
  }
 
 
 
  private void evictAssociated(EntityManager em, Object entity) {
    Class<?> clazz = entity.getClass();
    for (Field field : clazz.getDeclaredFields()) {

      /*
       * Evict ManyToOne
       */
      if (orm.isManyToOne(clazz, field.getName())) {
        try {
          Method getter = clazz.getDeclaredMethod("get"
              + StringUtilities.capitalize(field.getName()));
          Object associated = getter.invoke(entity);
          if (associated == null) {
            continue;
          }
          Class<?> associatedClazz = associated.getClass();
          Method idGetter = associatedClazz
              .getDeclaredMethod("getId");
          EntityManagerFactory emf = em.getEntityManagerFactory();
          Cache emfCache = emf.getCache();
          emfCache.evict(associated.getClass(),
              idGetter.invoke(associated));
        } catch (Exception e) {
          log.warn("Error", e);
        }
      }

      /*
       * Evict OneToMany or ManyToMany
       */
      if (orm.isOneToMany(clazz, field.getName()) || orm.isManyToMany(clazz, field.getName())) {
        try {
          Method getter = clazz.getDeclaredMethod("get"
              + StringUtilities.capitalize(field.getName()));
          Set<?> associatedSet = (Set<?>) getter.invoke(entity);

          for (Object associated : associatedSet) {
            Class<?> associatedClazz = associated.getClass();
            Method idGetter = associatedClazz
                .getDeclaredMethod("getId");
            em.getEntityManagerFactory()
                .getCache()
                .evict(associated.getClass(),
                    idGetter.invoke(associated));
          }
        } catch (Exception e) {
          log.warn("Error", e);
        }
      }
    }
  }
}
TOP

Related Classes of com.adaptrex.core.persistence.jpa.JPAModelInstance

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.