/**
* Copyright (c) 2011 - 2015, Lunifera GmbH (Gross Enzersdorf), Loetz KG (Heidelberg)
* 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:
* Florian Pirchner - Initial implementation
*/
package org.lunifera.dsl.dto.lib.services.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import org.eclipse.persistence.internal.jpa.EntityManagerImpl;
import org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork;
import org.eclipse.persistence.sessions.SessionEvent;
import org.eclipse.persistence.sessions.SessionEventAdapter;
import org.lunifera.dsl.dto.lib.IMapper;
import org.lunifera.dsl.dto.lib.IMapperAccess;
import org.lunifera.dsl.dto.lib.MappingContext;
import org.lunifera.dsl.dto.lib.services.IQuery;
import org.lunifera.dsl.dto.lib.services.jpa.metadata.EntityDelegate;
import org.lunifera.runtime.common.annotations.DtoUtils;
import org.lunifera.runtime.common.hash.HashUtil;
import org.lunifera.runtime.common.state.ISharedStateContext;
@SuppressWarnings("all")
public abstract class AbstractDTOService<DTO, ENTITY> implements
org.lunifera.dsl.dto.lib.services.IDTOService<DTO> {
private EntityManagerFactory emf;
protected IMapperAccess mapperAccess;
protected abstract Class<DTO> getDtoClass();
protected abstract DTO createDto();
protected abstract Class<ENTITY> getEntityClass();
protected abstract ENTITY createEntity();
protected abstract Object getId(DTO dto);
/**
* {@inherit doc}
*
*/
public DTO get(final Object id) {
javax.persistence.EntityManager em = emf.createEntityManager();
EntityDelegate<ENTITY> delegate = new EntityDelegate<ENTITY>(
getEntityClass(), em, 1);
// find the entity
DTO result = null;
try {
ENTITY entity = delegate.getEntity(id);
if (entity != null) {
IMapper<DTO, ENTITY> mapper = findToDtoMapper(getDtoClass(),
(Class<ENTITY>) entity.getClass());
MappingContext mappingContext = new MappingContext(true);
mappingContext.increaseLevel();
result = mapSingle(entity, mapper, mappingContext);
mappingContext.decreaseLevel();
mappingContext.flush();
}
} finally {
em.close();
}
return result;
}
protected DTO mapSingle(ENTITY entity, IMapper<DTO, ENTITY> mapper,
MappingContext context) {
DTO result;
try {
context.increaseLevel();
DTO cached = context.get(mapper.createDtoHash(entity));
if (cached != null) {
result = cached;
} else {
DTO dto = mapper.createDto();
mapper.mapToDTO(dto, entity, context);
result = dto;
}
} finally {
context.decreaseLevel();
}
return result;
}
/**
* See {@link IMapperAccess#getToDtoMapper(Class, Class)}
*
* @param dtoClass
* @param entityClass
* @return
*/
protected IMapper<DTO, ENTITY> findToDtoMapper(Class<DTO> dtoClass,
Class<ENTITY> entityClass) {
return (IMapper<DTO, ENTITY>) mapperAccess.getToDtoMapper(dtoClass,
entityClass);
}
/**
* See {@link IMapperAccess#getToEntityMapper(Class, Class)}
*
* @param dtoClass
* @param entityClass
* @return
*/
protected IMapper<DTO, ENTITY> findToEntityMapper(Class<DTO> dtoClass,
Class<ENTITY> entityClass) {
return (IMapper<DTO, ENTITY>) mapperAccess.getToEntityMapper(dtoClass,
entityClass);
}
/**
* {@inherit doc}
*
*/
public Collection<DTO> find(final IQuery query) {
javax.persistence.EntityManager em = emf.createEntityManager();
EntityDelegate<ENTITY> delegate = new EntityDelegate<ENTITY>(
getEntityClass(), em, PROP_MAX_COLLECTION_CONTENT);
List<DTO> result = new ArrayList<DTO>();
try {
MappingContext mappingContext = new MappingContext(true);
mappingContext.increaseLevel();
for (ENTITY entity : delegate.getAllEntities(query)) {
IMapper<DTO, ENTITY> mapper = findToDtoMapper(getDtoClass(),
(Class<ENTITY>) entity.getClass());
result.add(mapSingle(entity, mapper, mappingContext));
}
mappingContext.decreaseLevel();
mappingContext.flush();
} finally {
em.close();
}
return result;
}
/**
* {@inherit doc}
*
*/
public Collection<DTO> find(final IQuery query, final int startindex) {
javax.persistence.EntityManager em = emf.createEntityManager();
EntityDelegate<ENTITY> delegate = new EntityDelegate<ENTITY>(
getEntityClass(), em, PROP_MAX_COLLECTION_CONTENT);
List<DTO> result = new ArrayList<DTO>();
try {
MappingContext mappingContext = new MappingContext(true);
mappingContext.increaseLevel();
for (ENTITY entity : delegate.getAllEntities(query, startindex)) {
IMapper<DTO, ENTITY> mapper = findToDtoMapper(getDtoClass(),
(Class<ENTITY>) entity.getClass());
result.add(mapSingle(entity, mapper, mappingContext));
}
mappingContext.decreaseLevel();
mappingContext.flush();
} finally {
em.close();
}
return result;
}
/**
* {@inherit doc}
*
*/
public void update(final DTO dto) {
EntityManager em = emf.createEntityManager();
EntityTransaction txn = em.getTransaction();
MappingContext entityMappingContext = new MappingContext(false);
TransactionObserver entityTxnObserver = new TransactionObserver(
TransactionObserver.UPDATE, em, entityMappingContext);
MappingContext dtoMappingContext = new MappingContext(true);
dtoMappingContext.increaseLevel();
try {
txn.begin();
ENTITY entity = null;
Object id = getId(dto);
if (id != null) {
entity = em.find(getEntityClass(), id);
if (entity == null) {
entity = createEntity();
}
} else {
entity = createEntity();
}
IMapper<DTO, ENTITY> toEntityMapper = findToEntityMapper(
(Class<DTO>) dto.getClass(),
(Class<ENTITY>) entity.getClass());
// map dto to entity and persist
toEntityMapper.mapToEntity(dto, entity, entityMappingContext);
em.merge(entity);
IMapper<DTO, ENTITY> toDtoMapper = findToDtoMapper(
(Class<DTO>) dto.getClass(),
(Class<ENTITY>) entity.getClass());
// map the entity back to the dto since values may have
// changed in dto
toDtoMapper.mapToDTO(dto, entity, dtoMappingContext);
txn.commit();
txn = null;
} finally {
ISharedStateContext sharedState = dtoMappingContext
.getSharedState();
if (sharedState != null) {
if (txn == null) {
// put dtos in ISharedStateContext
dtoMappingContext.decreaseLevel();
dtoMappingContext.flush();
}
for (Object obj : entityTxnObserver.affected) {
// access with entity hash
Object affectedDto = entityMappingContext
.getMappingRoot(HashUtil.createObjectWithIdHash(
obj.getClass(), obj));
// access with dto hash
removeFromDirtyState(
HashUtil.createObjectWithIdHash(
affectedDto.getClass(), affectedDto),
sharedState);
}
}
entityTxnObserver.dispose();
em.close();
}
}
/**
* Removes the dto with the given key from the dirty state cache.
*
* @param dtoKey
* @param sharedState
*/
private void removeFromDirtyState(Object dtoKey,
ISharedStateContext sharedState) {
sharedState.getDirtyState().invalidate(dtoKey);
}
/**
* Removes the dto with the given key from the dirty state cache.
*
* @param dtoKey
* @param affectedDto
* @param sharedState
*/
private void removeFromSharedState(Object dtoKey, Object affectedDto,
ISharedStateContext sharedState) {
// try to dispose the dto. Will remove it from caches automatically.
if (!DtoUtils.invokeDisposeMethod(affectedDto)) {
sharedState.getDirtyState().invalidate(dtoKey);
sharedState.getGlobalDataState().invalidate(dtoKey);
}
}
/**
* {@inherit doc}
*
*/
public void delete(final DTO dto) {
javax.persistence.EntityManager em = emf.createEntityManager();
javax.persistence.EntityTransaction txn = em.getTransaction();
// create a txn observer to get all deleted elements
MappingContext entityMappingContext = new MappingContext(true);
entityMappingContext.increaseLevel();
TransactionObserver entityTxnObserver = new TransactionObserver(
TransactionObserver.DELETE, em, entityMappingContext);
try {
txn.begin();
// we need to do a mapping step first to get references from entity
// to DTOs
// then we know which dtos must be removed from shared context
IMapper<DTO, ENTITY> mapper = findToEntityMapper(
(Class<DTO>) dto.getClass(),
(Class<ENTITY>) getEntityClass());
// map dto to entity and persist
ENTITY entity = mapper.createEntity();
mapper.mapToEntity(dto, entity, entityMappingContext);
entity = em.find(getEntityClass(), getId(dto));
if (entity != null) {
em.remove(entity);
}
txn.commit();
txn = null;
} finally {
if (txn == null) {
// if using shared state, map the deleted entities to their dtos
// and remove them from the shared state
ISharedStateContext sharedState = entityMappingContext
.getSharedState();
if (sharedState != null) {
for (Object obj : entityTxnObserver.affected) {
// access with entity hash
Object affectedDto = entityMappingContext
.getMappingRoot(HashUtil
.createObjectWithIdHash(obj.getClass(),
obj));
// access with dto hash
removeFromSharedState(
HashUtil.createObjectWithIdHash(
affectedDto.getClass(), affectedDto),
affectedDto, sharedState);
}
}
}
entityTxnObserver.dispose();
em.close();
}
}
/**
* Binds the service {@link IMapperAccess} to this component. <br>
* The cardinality is ONE_TO_ONE
*
* @param mapper
* the service
*/
protected void bindMapperAccess(final IMapperAccess mapperAccess) {
this.mapperAccess = mapperAccess;
}
/**
* Unbinds the service from this component. <br>
* The cardinality is ONE_TO_ONE
*
* @param mapper
* the service
*/
protected void unbindMapperAccess(final IMapperAccess mapperAccess) {
this.mapperAccess = null;
}
/**
* Binds the service {@link javax.persistence.EntityManagerFactory} to this
* component. <br>
* The cardinality is ONE_TO_ONE
*
* @param emf
* the service
*/
protected void bindEmf(final EntityManagerFactory emf) {
this.emf = emf;
}
/**
* Unbinds the service from this component. <br>
* The cardinality is ONE_TO_ONE
*
* @param emf
* the service
*/
protected void unbindEmf(final EntityManagerFactory emf) {
this.emf = null;
}
/**
* @return the emf
*/
protected EntityManagerFactory getEmf() {
return emf;
}
@Override
public int size(IQuery query) {
javax.persistence.EntityManager em = emf.createEntityManager();
EntityDelegate<ENTITY> delegate = new EntityDelegate<ENTITY>(
getEntityClass(), em, 1);
int result = -1;
try {
result = delegate.getEntityCount(query);
} finally {
em.close();
}
return result;
}
@Override
public boolean contains(Object dto, IQuery query) {
javax.persistence.EntityManager em = emf.createEntityManager();
EntityDelegate<ENTITY> delegate = new EntityDelegate<ENTITY>(
getEntityClass(), em, 1);
boolean result = false;
try {
result = delegate.containsEntityIdentifier(dto, query);
} finally {
em.close();
}
return result;
}
@Override
public DTO getNext(DTO dto, IQuery query) {
javax.persistence.EntityManager em = emf.createEntityManager();
EntityDelegate<ENTITY> delegate = new EntityDelegate<ENTITY>(
getEntityClass(), em, 1);
DTO result = null;
try {
ENTITY entity = delegate.getNextEntity(getId(dto), query);
if (entity != null) {
IMapper<DTO, ENTITY> mapper = findToDtoMapper(getDtoClass(),
(Class<ENTITY>) entity.getClass());
MappingContext mappingContext = new MappingContext(true);
mappingContext.increaseLevel();
result = mapSingle(entity, mapper, mappingContext);
mappingContext.decreaseLevel();
mappingContext.flush();
}
} finally {
em.close();
}
return result;
}
@Override
public DTO getPrevious(DTO dto, IQuery query) {
javax.persistence.EntityManager em = emf.createEntityManager();
EntityDelegate<ENTITY> delegate = new EntityDelegate<ENTITY>(
getEntityClass(), em, 1);
DTO result = null;
try {
ENTITY entity = delegate.getPreviousEntity(getId(dto), query);
if (entity != null) {
IMapper<DTO, ENTITY> mapper = findToDtoMapper(getDtoClass(),
(Class<ENTITY>) entity.getClass());
MappingContext mappingContext = new MappingContext(true);
mappingContext.increaseLevel();
result = mapSingle(entity, mapper, mappingContext);
mappingContext.decreaseLevel();
mappingContext.flush();
}
} finally {
em.close();
}
return result;
}
@Override
public DTO getFirst(IQuery query) {
javax.persistence.EntityManager em = emf.createEntityManager();
EntityDelegate<ENTITY> delegate = new EntityDelegate<ENTITY>(
getEntityClass(), em, 1);
DTO result = null;
try {
ENTITY entity = delegate.getFirstEntity(query);
if (entity != null) {
IMapper<DTO, ENTITY> mapper = findToDtoMapper(getDtoClass(),
(Class<ENTITY>) entity.getClass());
MappingContext mappingContext = new MappingContext(true);
mappingContext.increaseLevel();
result = mapSingle(entity, mapper, mappingContext);
mappingContext.decreaseLevel();
mappingContext.flush();
}
} finally {
em.close();
}
return result;
}
@Override
public DTO getLast(IQuery query) {
javax.persistence.EntityManager em = emf.createEntityManager();
EntityDelegate<ENTITY> delegate = new EntityDelegate<ENTITY>(
getEntityClass(), em, 1);
DTO result = null;
try {
ENTITY entity = delegate.getLastEntity(query);
if (entity != null) {
IMapper<DTO, ENTITY> mapper = findToDtoMapper(getDtoClass(),
(Class<ENTITY>) entity.getClass());
MappingContext mappingContext = new MappingContext(true);
mappingContext.increaseLevel();
result = mapSingle(entity, mapper, mappingContext);
mappingContext.decreaseLevel();
mappingContext.flush();
}
} finally {
em.close();
}
return result;
}
@Override
public boolean isFirst(DTO dto, IQuery query) {
javax.persistence.EntityManager em = emf.createEntityManager();
EntityDelegate<ENTITY> delegate = new EntityDelegate<ENTITY>(
getEntityClass(), em, 1);
List<DTO> result = new ArrayList<DTO>();
try {
String firstId = (String) delegate.getFirstEntityIdentifier(query);
if (firstId != null && firstId.equals(getId(dto))) {
return true;
}
} finally {
em.close();
}
return false;
}
@Override
public boolean isLast(DTO dto, IQuery query) {
javax.persistence.EntityManager em = emf.createEntityManager();
EntityDelegate<ENTITY> delegate = new EntityDelegate<ENTITY>(
getEntityClass(), em, 1);
List<DTO> result = new ArrayList<DTO>();
try {
String lastId = (String) delegate.getLastEntityIdentifier(query);
if (lastId != null && lastId.equals(getId(dto))) {
return true;
}
} finally {
em.close();
}
return false;
}
@Override
public int indexOf(DTO dto, IQuery query) {
return 1;
}
@Override
public DTO getByIndex(int index, IQuery query) {
javax.persistence.EntityManager em = emf.createEntityManager();
EntityDelegate<ENTITY> delegate = new EntityDelegate<ENTITY>(
getEntityClass(), em, 1);
DTO result = null;
try {
MappingContext mappingContext = new MappingContext(true);
mappingContext.increaseLevel();
for (ENTITY entity : delegate.getAllEntities(query, index)) {
IMapper<DTO, ENTITY> mapper = findToDtoMapper(getDtoClass(),
(Class<ENTITY>) entity.getClass());
result = mapSingle(entity, mapper, mappingContext);
break;
}
mappingContext.decreaseLevel();
mappingContext.flush();
} finally {
em.close();
}
return result;
}
@Override
public List<DTO> getByIndex(int startIndex, int numberOfItems, IQuery query) {
javax.persistence.EntityManager em = emf.createEntityManager();
EntityDelegate<ENTITY> delegate = new EntityDelegate<ENTITY>(
getEntityClass(), em, numberOfItems);
List<DTO> result = new ArrayList<DTO>();
try {
MappingContext mappingContext = new MappingContext(true);
mappingContext.increaseLevel();
for (ENTITY entity : delegate.getAllEntities(query, startIndex)) {
IMapper<DTO, ENTITY> mapper = findToDtoMapper(getDtoClass(),
(Class<ENTITY>) entity.getClass());
result.add(mapSingle(entity, mapper, mappingContext));
}
mappingContext.decreaseLevel();
mappingContext.flush();
} finally {
em.close();
}
return result;
}
private static class TransactionObserver extends SessionEventAdapter {
private static final int UPDATE = 0;
private static final int DELETE = 1;
private List<Object> affected = new ArrayList<Object>();
private EntityManager em;
private MappingContext context;
private int type;
public TransactionObserver(int type, EntityManager em,
MappingContext context) {
this.type = type;
this.em = em;
this.context = context;
((EntityManagerImpl) em).getActiveSession().getEventManager()
.addListener(this);
}
@Override
public void postCommitUnitOfWork(SessionEvent event) {
RepeatableWriteUnitOfWork uow = (RepeatableWriteUnitOfWork) event
.getSource();
if (type == UPDATE) {
for (Object object : uow.getCloneMapping().values()) {
affected.add(object);
}
} else if (type == DELETE) {
for (Object object : uow.getDeletedObjects().values()) {
affected.add(object);
}
}
}
public void dispose() {
((EntityManagerImpl) em).getActiveSession().getEventManager()
.removeListener(this);
}
}
}