org.picketlink.idm.jpa.internal.mappers.EntityMapper Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source
*
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* 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 org.picketlink.idm.jpa.internal.mappers;
import org.picketlink.common.properties.Property;
import org.picketlink.common.properties.query.NamedPropertyCriteria;
import org.picketlink.common.properties.query.PropertyQueries;
import org.picketlink.common.properties.query.TypedPropertyCriteria;
import org.picketlink.idm.IdentityManagementException;
import org.picketlink.idm.jpa.annotations.OwnerReference;
import org.picketlink.idm.jpa.annotations.RelationshipMember;
import org.picketlink.idm.jpa.annotations.entity.IdentityManaged;
import org.picketlink.idm.jpa.annotations.entity.MappedAttribute;
import org.picketlink.idm.jpa.internal.AttributeList;
import org.picketlink.idm.jpa.internal.JPAIdentityStore;
import org.picketlink.idm.model.AttributedType;
import org.picketlink.idm.model.Partition;
import org.picketlink.idm.model.Relationship;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import static java.util.Map.Entry;
import static org.picketlink.common.reflection.Reflections.newInstance;
import static org.picketlink.idm.IDMMessages.MESSAGES;
/**
* This class holds all the mapping configuration for a specific JPA Entity and their corresponding IDM model
* classes. A specific JPA entity can be mapped to and from different IDM model classes.
Each {@link
* EntityMapping} holds the specific mapping for a IDM model type.
*
* @author pedroigor
*/
public class EntityMapper {
private final List entityMappings;
private final Class> entityType;
private final JPAIdentityStore store;
public EntityMapper(Class> entityType, JPAIdentityStore jpaIdentityStore) {
this.entityType = entityType;
this.store = jpaIdentityStore;
List mappings = new ArrayList();
for (ModelMapper modelMapper : getModelMappers()) {
mappings.addAll(modelMapper.createMapping(entityType));
}
if (mappings.isEmpty()) {
throw new IdentityManagementException("Entity [" + entityType + "] does not have any mapping. Check if it is properly mapped with the JPA Store mapping annotations.");
}
this.entityMappings = Collections.unmodifiableList(mappings);
}
public Object persist(AttributedType attributedType, EntityManager entityManager) {
Object entity = getEntityInstance(attributedType, entityManager);
if (entity != null) {
EntityMapping entityMapping = getMappingsFor(attributedType.getClass());
for (Property property : entityMapping.getProperties().keySet()) {
Object propertyValue = property.getValue(attributedType);
Property mappedProperty = entityMapping.getProperties().get(property);
Object mappedValue = propertyValue;
if (mappedProperty.getAnnotatedElement().isAnnotationPresent(OwnerReference.class)) {
AttributedType ownerType = (AttributedType) propertyValue;
if (ownerType == null || ownerType.getId() == null) {
if (isPartitionSupported(ownerType.getClass())) {
throw new IdentityManagementException("Owner does not exist or was not provided.");
}
}
mappedValue = this.store.getOwnerEntity(ownerType, mappedProperty, entityManager);
} else {
// if the attributeProperty maps to a mapped type is because we have a many-to-one relationship
// this is the case when a type has a hierarchy
if (AttributedType.class.isInstance(propertyValue)) {
AttributedType ownerType = (AttributedType) propertyValue;
if (this.store.isMappedType(mappedProperty.getJavaClass())) {
if (ownerType != null) {
mappedValue = entityManager.find(mappedProperty.getJavaClass(), ownerType.getId());
}
}
}
}
mappedProperty.setValue(entity, mappedValue);
}
entityManager.persist(entity);
}
return entity;
}
public Object updateEntity(AttributedType attributedType, EntityManager entityManager) {
Object entityInstance = getEntityInstance(attributedType, entityManager);
if (entityInstance != null) {
if (List.class.isInstance(entityInstance)) {
List attributes = (List) entityInstance;
if (AttributeList.class.isInstance(entityInstance)) {
Iterator originalIterator = ((AttributeList) attributes).getOriginalList().iterator();
while (originalIterator.hasNext()) {
Object attribute = originalIterator.next();
if (!attributes.contains(attribute)) {
entityManager.remove(attribute);
}
}
}
Iterator iterator = attributes.iterator();
while (iterator.hasNext()) {
updateEntity(attributedType, iterator.next(), entityManager);
}
} else {
updateEntity(attributedType, entityInstance, entityManager);
}
}
return entityInstance;
}
public P createType(Object entityInstance, EntityManager entityManager) {
P attributedType = null;
if (entityInstance != null) {
// if the instance is not of the same or a child of the mappper's type, we assume a one-to-one relationship.
// in this case we need to start to create the type and populate it of the referenced owner entity.
if (!getEntityType().equals(entityInstance.getClass()) && !getEntityType().isAssignableFrom(entityInstance.getClass())) {
EntityMapper entityMapper = this.store.getMapperForEntity(entityInstance.getClass());
Entry property = entityMapper.getProperty(OwnerReference.class);
if (property != null) {
entityInstance = property.getValue().getValue(entityInstance);
}
}
try {
attributedType = (P) newInstance(entityInstance.getClass(), getTypeProperty().getValue(entityInstance).toString());
EntityMapping entityMapping = getMappingsFor(attributedType.getClass());
for (Property property : entityMapping.getProperties().keySet()) {
Property mappedProperty = entityMapping.getProperties().get(property);
Object mappedValue = mappedProperty.getValue(entityInstance);
Object propertyValue = mappedValue;
if (mappedProperty.getAnnotatedElement().isAnnotationPresent(OwnerReference.class)) {
if (mappedValue == null) {
if (isPartitionSupported(property.getJavaClass())) {
throw new IdentityManagementException("Owner does not exists or was not provided.");
}
} else {
EntityMapper entityMapper = this.store.getMapperForEntity(mappedValue.getClass());
propertyValue = entityMapper.createType(mappedValue, entityManager);
}
} else {
// if the property maps to a mapped type is because we have a many-to-one relationship
// this is the case when a type has a hierarchy
if (this.store.isMappedType(mappedProperty.getJavaClass())) {
propertyValue = createType(mappedValue, entityManager);
}
}
property.setValue(attributedType, propertyValue);
}
if (isRoot()) {
for (EntityMapper finalMapper : this.store.getMapperFor(attributedType.getClass())) {
if (!finalMapper.isRoot()) {
for (Object child : getAssociatedEntities(attributedType, finalMapper, entityManager)) {
finalMapper.populate(attributedType, child, entityManager);
}
}
}
}
} catch (Exception e) {
throw new IdentityManagementException("Could not create [" + attributedType + " from entity [" + entityInstance + "].", e);
}
}
return attributedType;
}
public Entry getProperty(Class> attributedType, String propertyName) {
EntityMapping entityMapping = getMappingsFor(attributedType);
for (Entry property : entityMapping.getProperties().entrySet()) {
if (property.getKey().getName().equals(propertyName)) {
return property;
}
}
return null;
}
public List getEntityMappings() {
return this.entityMappings;
}
public Class> getEntityType() {
return this.entityType;
}
public Entry getProperty(Class> attributedType,
Class extends Annotation> annotation) {
EntityMapping entityMapping = getMappingsFor(attributedType);
if (entityMapping != null) {
for (Entry property : entityMapping.getProperties().entrySet()) {
if (property.getValue().getAnnotatedElement().isAnnotationPresent(annotation)) {
return property;
}
}
}
return null;
}
public Entry getProperty(Class extends Annotation> annotation) {
for (EntityMapping entityMapping : getEntityMappings()) {
for (Entry property : entityMapping.getProperties().entrySet()) {
if (property.getValue().getAnnotatedElement().isAnnotationPresent(annotation)) {
return property;
}
}
}
return null;
}
public EntityMapping getMappingsFor(Class> attributedType) {
for (EntityMapping entityMapping : getEntityMappings()) {
if (entityMapping.getSupportedType().equals(attributedType)) {
return entityMapping;
}
}
for (EntityMapping entityMapping : getEntityMappings()) {
if (entityMapping.getSupportedType().isAssignableFrom(attributedType)) {
return entityMapping;
}
}
for (EntityMapping entityMapping : getEntityMappings()) {
if (attributedType.isAssignableFrom(entityMapping.getSupportedType())) {
return entityMapping;
}
}
return null;
}
public boolean isRoot() {
return getTypeProperty() != null;
}
public List getAssociatedEntities(AttributedType attributedType, EntityMapper entityMapper, EntityManager entityManager) {
if (!entityMapper.getEntityType().isAnnotationPresent(IdentityManaged.class)) {
return Collections.emptyList();
}
StringBuilder hql = new StringBuilder();
hql.append("from ").append(entityMapper.getEntityType().getName()).append(" o where ");
Entry ownerProperty = entityMapper.getProperty(attributedType.getClass(), OwnerReference.class);
if (ownerProperty == null) {
return Collections.emptyList();
}
hql.append(" o.").append(ownerProperty.getValue().getName()).append(" = :owner");
Query childQuery = entityManager.createQuery(hql.toString());
Object ownerEntity = this.store.getOwnerEntity(attributedType, ownerProperty.getValue(), entityManager);
childQuery.setParameter("owner", ownerEntity);
return childQuery.getResultList();
}
public Object createEntity() {
try {
return newInstance(getEntityType(), getEntityType().getName());
} catch (Exception e) {
throw MESSAGES.instantiationError(getEntityType(), e);
}
}
private void populate(V attributedType, Object entityInstance, EntityManager entityManager) {
if (getEntityType().isAnnotationPresent(MappedAttribute.class)) {
MappedAttribute mappedAttribute = getEntityType().getAnnotation(MappedAttribute.class);
Property