/* * Copyright 2004-2009 the original author or authors. * * 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.compass.gps.device.hibernate.lifecycle; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.transaction.Synchronization; import org.compass.core.CompassSession; import org.compass.core.mapping.Cascade; import org.compass.core.mapping.CompassMapping; import org.compass.core.mapping.Mapping; import org.compass.core.mapping.ResourceMapping; import org.compass.core.mapping.osem.ClassMapping; import org.compass.core.mapping.osem.ClassPropertyMetaDataMapping; import org.compass.core.spi.InternalCompass; import org.compass.core.util.Assert; import org.compass.core.util.FieldInvoker; import org.compass.gps.spi.CompassGpsInterfaceDevice; import org.hibernate.EntityMode; import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.event.EventSource; import org.hibernate.metadata.ClassMetadata; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.type.CollectionType; import org.hibernate.type.Type; /** * @author kimchy */ public abstract class HibernateEventListenerUtils { public static void registerRemovalHook(EventSource eventSource, Map<Object, Collection> pendingMap, Object entity) { eventSource.getTransaction().registerSynchronization(new RemoveFromPending(pendingMap, entity)); } public static Collection getAssociatedDependencies(Object entity, Map<Object, Collection> pendingMap) { Set dependencies = new HashSet(); for (Collection other : pendingMap.values()) { if (other.contains(entity)) { dependencies.addAll(other); } } return dependencies; } public static Collection getUnpersistedCascades(CompassGpsInterfaceDevice compassGps, Object entity, SessionFactoryImplementor sessionFactory, Cascade cascade, Collection visited) { if (visited.contains(entity)) { return Collections.EMPTY_SET; } visited.add(entity); ClassMetadata classMetadata = sessionFactory.getClassMetadata(entity.getClass()); if (classMetadata == null) { for (Iterator iter = sessionFactory.getAllClassMetadata().values().iterator(); iter.hasNext();) { ClassMetadata temp = (ClassMetadata) iter.next(); if (entity.getClass().equals(temp.getMappedClass(EntityMode.POJO))) { classMetadata = temp; break; } } } Assert.notNull(classMetadata, "Failed to lookup Hibernate ClassMetadata for entity [" + entity + "]"); String entityName = classMetadata.getEntityName(); EntityPersister persister = sessionFactory.getEntityPersister(entityName); CompassMapping compassMapping = ((InternalCompass) compassGps.getIndexCompass()).getMapping(); ClassMapping classMapping = (ClassMapping) compassMapping.getMappingByClass(entity.getClass()); if (classMapping == null) { return Collections.EMPTY_SET; } // CascadeStyle[] cascadeStyles = persister.getEntityMetamodel().getCascadeStyles(); String[] propertyNames = persister.getPropertyNames(); Type[] types = persister.getPropertyTypes(); Set dependencies = new HashSet(); for (int i = 0, len = propertyNames.length; i < len; i++) { // property cascade includes save/update? // CascadeStyle cascadeStyle = cascadeStyles[i]; // if (!cascadeStyle.doCascade(CascadingAction.SAVE_UPDATE)) { // continue; // } // property is mapped in Compass? String name = propertyNames[i]; Mapping mapping = classMapping.getMapping(name); if (mapping == null) { continue; } // property value is not null? Object propertyValue = persister.getPropertyValue(entity, name, EntityMode.POJO); if (propertyValue == null) { continue; } // find actual property type // todo may not be correct see http://www.hibernate.org/hib_docs/v3/api/org/hibernate/type/EntityType.html#getReturnedClass() // todo may need to use class name string comparison instead Class propertyType; Type type = types[i]; boolean collection = false; if (type instanceof CollectionType) { CollectionType collectionType = (CollectionType) type; propertyType = persister.getFactory().getCollectionPersister(collectionType.getRole()).getElementType().getReturnedClass(); collection = true; } else { propertyType = type.getReturnedClass(); } // Mirroring is cascaded for this property? if (!compassGps.hasMappingForEntityForMirror(propertyType, cascade)) { continue; } // find dependent unpersisted property value(s) ResourceMapping propertyTypeMapping = compassMapping.getMappingByClass(propertyType); Mapping[] idMappings = propertyTypeMapping.getIdMappings(); for (int j = 0, jlen = idMappings.length; j < jlen; j++) { ClassPropertyMetaDataMapping idMapping = (ClassPropertyMetaDataMapping) idMappings[j]; String idPropertyName = idMapping.getPropertyName(); try { FieldInvoker idGetter = new FieldInvoker(propertyType, idPropertyName).prepare(); Object value = new FieldInvoker(entity.getClass(), name).prepare().get(entity); if (collection) { for (Iterator iter = ((Collection) value).iterator(); iter.hasNext();) { Object obj = iter.next(); Object id = idGetter.get(obj); if (id == null) { dependencies.add(obj); } dependencies.addAll(getUnpersistedCascades(compassGps, obj, sessionFactory, cascade, visited)); } } else { Object id = idGetter.get(value); if (id == null) { dependencies.add(value); } dependencies.addAll(getUnpersistedCascades(compassGps, value, sessionFactory, cascade, visited)); } } catch (Exception e) { // ignore } } } return dependencies; } public static void persistPending(CompassSession session, Object entity, Map<Object, Collection> pendingMap, boolean create) { for (Iterator iter = pendingMap.keySet().iterator(); iter.hasNext();) { Object pending = iter.next(); Collection dependencies = pendingMap.get(pending); if (dependencies.remove(entity)) { if (dependencies.isEmpty()) { if (create) { session.create(pending); } else { session.save(pending); } iter.remove(); } } } } private static class RemoveFromPending implements Synchronization { private Map<Object, Collection> pendingMap; private Object entity; public RemoveFromPending(Map<Object, Collection> pendingMap, Object entity) { this.pendingMap = pendingMap; this.entity = entity; } private void removePendingDependencies(Object entity, Map<Object, Collection> pendingMap) { for (Iterator<Object> iter = pendingMap.keySet().iterator(); iter.hasNext();) { Object pending = iter.next(); if (pending == entity) { iter.remove(); break; } Collection dependencies = pendingMap.get(pending); dependencies.remove(entity); if (dependencies.isEmpty()) { iter.remove(); } } } public void afterCompletion(int i) { removePendingDependencies(entity, pendingMap); } public void beforeCompletion() { } } }