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

io.pelle.mango.db.dao.BaseEntityDAO Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2013 Christian Pelster.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Christian Pelster - initial API and implementation
 */
package io.pelle.mango.db.dao;

import static io.pelle.mango.client.base.vo.query.SelectQuery.selectFrom;

import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.ConstructorUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import com.google.common.base.Joiner;
import com.google.common.base.Optional;

import io.pelle.mango.client.base.db.vos.IInfoVOEntity;
import io.pelle.mango.client.base.vo.IBaseEntity;
import io.pelle.mango.client.base.vo.query.CountQuery;
import io.pelle.mango.client.base.vo.query.DeleteQuery;
import io.pelle.mango.client.base.vo.query.SelectQuery;
import io.pelle.mango.client.base.vo.query.expressions.ExpressionFactory;
import io.pelle.mango.db.IUser;
import io.pelle.mango.db.copy.ObjectFieldDescriptor;
import io.pelle.mango.db.copy.ObjectFieldIterator;
import io.pelle.mango.db.query.ServerCountQuery;
import io.pelle.mango.db.util.DBUtil;
import io.pelle.mango.db.util.EntityVOMapper;
import io.pelle.mango.db.voquery.EntityClassQuery;
import io.pelle.mango.server.base.IBaseClientEntity;

@Component
public class BaseEntityDAO extends BaseDAO {

	private static Logger LOG = Logger.getLogger(BaseEntityDAO.class);

	@Override
	public  T create(T entity) {

		if (entity instanceof IBaseClientEntity) {

			populateClients(((IBaseClientEntity) entity), new ArrayList());

			LOG.debug(String.format("creating entity '%s' for client '%s'", entity.getClass().getName(), getCurrentUser().getClient()));
		} else {
			LOG.debug(String.format("creating entity '%s'", entity.getClass().getName()));
		}

		if (entity instanceof IInfoVOEntity) {

			IInfoVOEntity infoEntity = (IInfoVOEntity) entity;

			if (SecurityContextHolder.getContext().getAuthentication() != null && SecurityContextHolder.getContext().getAuthentication().getPrincipal() != null) {
				infoEntity.setUpdateUser(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
				infoEntity.setCreateUser(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
			}

			Date now = new Date();
			infoEntity.setCreateDate(now);
			infoEntity.setUpdateDate(now);
		}

		T result = mergeRecursive(entity);

		return result;

	}

	@Override
	public  void delete(T entity) {
		LOG.debug(String.format("deleting entity '%s' with id '%d'", entity.getClass().getName(), entity.getId()));

		// TODO warn if entities with embedded element collections are deleted

		DeleteQuery query = DeleteQuery.deleteFrom(entity.getClass()).where(ExpressionFactory.createLongExpression(entity.getClass(), IBaseEntity.ID_FIELD_NAME, entity.getId()));
		deleteQuery(query);
	}

	private void deleteTables(List tableNames) {

		for (String tableName : tableNames) {
			String nativeDeleteQuery = String.format("delete from %s", tableName);
			entityManager.createNativeQuery(nativeDeleteQuery).executeUpdate();
		}
	}

	@Override
	public  void deleteAll(Class entityClass) {

		if (LOG.isDebugEnabled()) {
			LOG.debug(String.format("deleting everything for entity '%s'", entityClass.getName()));
		}

		List elementCollections = EntityClassQuery.createQuery(entityClass).getElementCollections();
		if (LOG.isDebugEnabled() && !elementCollections.isEmpty()) {
			LOG.debug(String.format("deleting element collections '%s' for entity '%s'", Joiner.on(", ").join(elementCollections), entityClass.getName()));
			deleteTables(elementCollections);
		}

		List oneToManyJoinTables = EntityClassQuery.createQuery(entityClass).getOneToManyJoinTables();
		if (LOG.isDebugEnabled() && !oneToManyJoinTables.isEmpty()) {
			LOG.debug(String.format("deleting one to many join tables '%s' for entity '%s'", Joiner.on(", ").join(oneToManyJoinTables), entityClass.getName()));
			deleteTables(oneToManyJoinTables);
		}

		DeleteQuery query = DeleteQuery.deleteFrom(entityClass);
		deleteQuery(query);
	}

	@SuppressWarnings("unchecked")
	public  List getAll(Class entityClass) {
		return (List) getResultListInternal(selectFrom(entityClass), entityManager);
	}

	private IUser getCurrentUser() {
		try {
			return (IUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

		} catch (Exception e) {
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	private void populateClients(IBaseClientEntity clientEntity, List visited) {
		try {
			clientEntity.setClient(getCurrentUser().getClient());
			visited.add(clientEntity);

			for (Map.Entry entry : ((Map) PropertyUtils.describe(clientEntity)).entrySet()) {

				String propertyName = entry.getKey();
				PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(clientEntity, propertyName);

				if (propertyDescriptor != null) {
					Object attribute = PropertyUtils.getSimpleProperty(clientEntity, propertyName);

					if (attribute != null && IBaseClientEntity.class.isAssignableFrom(attribute.getClass())) {
						if (!visited.contains(attribute)) {
							populateClients((IBaseClientEntity) attribute, visited);
						}
					}

					if (attribute != null && List.class.isAssignableFrom(attribute.getClass())) {
						List list = (List) attribute;

						for (Object listElement : list) {
							if (IBaseClientEntity.class.isAssignableFrom(listElement.getClass())) {
								if (!visited.contains(attribute)) {
									populateClients((IBaseClientEntity) listElement, visited);
								}
							}
						}
					}
				}
			}
		} catch (Exception e) {
			throw new RuntimeException("error setting client", e);
		}
	}

	@Override
	public  T read(long id, Class entityClass) {

		T entity = this.entityManager.find(entityClass, id);

		if (entity instanceof IBaseClientEntity) {

			IUser user = getCurrentUser();

			LOG.debug(String.format("retrieving entity '%s' with id %d for client '%s'", entityClass.getName(), id, getCurrentUser().getClient()));

			if (((IBaseClientEntity) entity).getClient().getId() == user.getClient().getId()) {
				return entity;
			} else {
				throw new AccessDeniedException(String.format("entity '%s' not allowed for client '%s'", entity, user.getClient()));
			}
		} else {
			LOG.debug(String.format("retrieving entity '%s' with id %d", entityClass.getName(), id));

			return entity;
		}

	}

	@Override
	public  T save(T entity) {

		if (entity instanceof IBaseClientEntity) {
			((IBaseClientEntity) entity).setClient(getCurrentUser().getClient());

			LOG.debug(String.format("saving '%s' for client '%s'", entity.getClass(), getCurrentUser().getClient()));
		} else {
			// throw new AccessDeniedException(String.format(
			// "entity '%s' not allowed for client '%s'", entity,
			// getCurrentUser().getClient()));
		}

		if (entity instanceof IInfoVOEntity) {

			IInfoVOEntity infoEntity = (IInfoVOEntity) entity;

			if (SecurityContextHolder.getContext().getAuthentication() != null) {

				Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

				if (principal != null) {
					infoEntity.setUpdateUser(principal.toString());
				}
			}

			infoEntity.setUpdateDate(new Date());
		}

		T result = mergeRecursive(entity);

		return result;
	}

	private  T mergeRecursive(T entity) {
		return mergeRecursive(entity, "/");
	}

	@SuppressWarnings("unchecked")
	private  T mergeRecursive(T entity, String currentPath) {

		// TODO use dirty paths?
		for (ObjectFieldDescriptor fieldDescriptor : new ObjectFieldIterator(entity)) {

			Object sourceValue = fieldDescriptor.getSourceValue(entity);
			if (sourceValue instanceof IBaseEntity) {

				checkLoaded(entity, fieldDescriptor);

				IBaseEntity entityAttribute = (IBaseEntity) sourceValue;
				IBaseEntity mergedEntityAttribute = mergeRecursive(entityAttribute, currentPath);

				try {
					fieldDescriptor.getSourceWriteMethod().invoke(entity, mergedEntityAttribute);
				} catch (Exception e) {
					throw new RuntimeException(e);
				}

			} else if (sourceValue instanceof List) {

				checkLoaded(entity, fieldDescriptor);

				List sourceList = (List) sourceValue;

				for (int i = 0; i < sourceList.size(); i++) {

					Object listItem = sourceList.get(i);

					if (listItem instanceof IBaseEntity) {
						IBaseEntity baseEntityListItem = (IBaseEntity) listItem;
						IBaseEntity mergedEntityListItem = mergeRecursive(baseEntityListItem, currentPath);
						sourceList.set(i, mergedEntityListItem);
					} else {
						sourceList.set(i, listItem);
					}
				}

				continue;
			}
		}

		return this.entityManager.merge(entity);

	}

	private  void checkLoaded(T entity, ObjectFieldDescriptor fieldDescriptor) {
		if (!entity.isNew() && !entity.getMetadata().isLoaded(fieldDescriptor.getFieldName()) && entity.getMetadata().hasChanges()) {
			throw new RuntimeException(String.format("entity '%s' is dirty but has unloaded attribute '%s'", entity.getClass(), fieldDescriptor.getFieldName()));
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public  List filter(SelectQuery selectQuery) {
		return (List) getResultListInternal(selectQuery, entityManager);
	}

	@Override
	public  Optional read(SelectQuery selectQuery) {
		List result = filter(selectQuery);

		if (result.size() == 1) {
			return Optional.of(result.get(0));
		} else {
			return Optional. absent();
		}
	}

	@Override
	public  long count(CountQuery countQuery) {
		return (long) entityManager.createQuery(ServerCountQuery.adapt(countQuery).getJPQL(EntityVOMapper.getInstance())).getSingleResult();
	}

	@Override
	public  long count(Class entityClass) {
		return count(CountQuery.countFrom(entityClass));
	}

	@Override
	public  void delete(Class entityClass, long id) {
		T entity = read(id, entityClass);
		delete(entity);
	}

	@Override
	public  List searchByNaturalKey(Class entityClass, String naturalKey) {
		List result = filter(DBUtil.getNaturalKeyQuery(entityClass, naturalKey));
		return result;
	}

	@Override
	public  T getNewVO(String className, Map properties) {

		Class entityClass = EntityVOMapper.getInstance().getEntityClass(className);

		try {
			T entity = (T) ConstructorUtils.invokeConstructor(entityClass, new Object[0]);

			return entity;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

}