/*
* 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.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.compass.core.CompassCallbackWithoutResult;
import org.compass.core.CompassException;
import org.compass.core.CompassSession;
import org.compass.core.mapping.Cascade;
import org.compass.gps.device.hibernate.HibernateGpsDevice;
import org.compass.gps.device.hibernate.HibernateGpsDeviceException;
import org.compass.gps.spi.CompassGpsInterfaceDevice;
import org.hibernate.engine.CollectionEntry;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.event.EventSource;
import org.hibernate.event.PostDeleteEvent;
import org.hibernate.event.PostDeleteEventListener;
import org.hibernate.event.PostInsertEvent;
import org.hibernate.event.PostInsertEventListener;
import org.hibernate.event.PostUpdateEvent;
import org.hibernate.event.PostUpdateEventListener;
/**
* A default implementation for Hibernate lifecycle callbacks.
*
* @author kimchy
* @see org.compass.gps.device.hibernate.lifecycle.DefaultHibernateEntityLifecycleInjector
*/
public class HibernateEventListener implements PostInsertEventListener, PostUpdateEventListener, PostDeleteEventListener {
protected final Log log = LogFactory.getLog(getClass());
protected final HibernateGpsDevice device;
protected final HibernateMirrorFilter mirrorFilter;
protected final boolean marshallIds;
protected final boolean pendingCascades;
protected final boolean processCollections;
private Map<Object, Collection> pendingCreate = Collections.synchronizedMap(new IdentityHashMap<Object, Collection>());
private Map<Object, Collection> pendingSave = Collections.synchronizedMap(new IdentityHashMap<Object, Collection>());
public HibernateEventListener(HibernateGpsDevice device, boolean marshallIds, boolean pendingCascades, boolean processCollections) {
this.device = device;
this.mirrorFilter = device.getMirrorFilter();
this.marshallIds = marshallIds;
this.pendingCascades = pendingCascades;
this.processCollections = processCollections;
}
public void onPostInsert(final PostInsertEvent postInsertEvent) {
if (!device.shouldMirrorDataChanges() || device.isPerformingIndexOperation()) {
return;
}
final CompassGpsInterfaceDevice compassGps = (CompassGpsInterfaceDevice) device.getGps();
final Object entity = postInsertEvent.getEntity();
if (!compassGps.hasMappingForEntityForMirror(entity.getClass(), Cascade.CREATE)) {
return;
}
if (mirrorFilter != null) {
if (mirrorFilter.shouldFilterInsert(postInsertEvent)) {
return;
}
}
try {
if (log.isTraceEnabled()) {
log.trace(device.buildMessage("Creating [" + entity + "]"));
}
compassGps.executeForMirror(new CompassCallbackWithoutResult() {
protected void doInCompassWithoutResult(CompassSession session) throws CompassException {
doInsert(session, postInsertEvent, entity, compassGps);
}
});
} catch (Exception e) {
if (device.isIgnoreMirrorExceptions()) {
log.error(device.buildMessage("Failed while creating [" + entity + "]"), e);
} else {
throw new HibernateGpsDeviceException(device.buildMessage("Failed while creating [" + entity + "]"), e);
}
}
}
public void onPostUpdate(final PostUpdateEvent postUpdateEvent) {
if (!device.shouldMirrorDataChanges() || device.isPerformingIndexOperation()) {
return;
}
final CompassGpsInterfaceDevice compassGps = (CompassGpsInterfaceDevice) device.getGps();
final Object entity = postUpdateEvent.getEntity();
if (!compassGps.hasMappingForEntityForMirror(entity.getClass(), Cascade.SAVE)) {
return;
}
if (mirrorFilter != null) {
if (mirrorFilter.shouldFilterUpdate(postUpdateEvent)) {
return;
}
}
Collection<CollectionEntry> collectionsBefore = null;
if (processCollections) {
collectionsBefore = new HashSet<CollectionEntry>(postUpdateEvent.getSession().getPersistenceContext().getCollectionEntries().values());
}
try {
if (log.isTraceEnabled()) {
log.trace(device.buildMessage("Updating [" + entity + "]"));
}
compassGps.executeForMirror(new CompassCallbackWithoutResult() {
protected void doInCompassWithoutResult(CompassSession session) throws CompassException {
doUpdate(session, compassGps, entity, postUpdateEvent.getSession());
}
});
} catch (Exception e) {
if (device.isIgnoreMirrorExceptions()) {
log.error(device.buildMessage("Failed while updating [" + entity + "]"), e);
} else {
throw new HibernateGpsDeviceException(device.buildMessage("Failed while updating [" + entity + "]"), e);
}
} finally {
if (processCollections) {
Collection<CollectionEntry> collectionsAfter = postUpdateEvent.getSession().getPersistenceContext().getCollectionEntries().values();
for (CollectionEntry collection : collectionsAfter) {
if (!collectionsBefore.contains(collection)) {
collection.setProcessed(true);
}
}
}
}
}
public void onPostDelete(PostDeleteEvent postDeleteEvent) {
if (!device.shouldMirrorDataChanges() || device.isPerformingIndexOperation()) {
return;
}
CompassGpsInterfaceDevice compassGps = (CompassGpsInterfaceDevice) device.getGps();
final Object entity = postDeleteEvent.getEntity();
if (!compassGps.hasMappingForEntityForMirror(entity.getClass(), Cascade.DELETE)) {
return;
}
if (mirrorFilter != null) {
if (mirrorFilter.shouldFilterDelete(postDeleteEvent)) {
return;
}
}
try {
if (log.isTraceEnabled()) {
log.trace(device.buildMessage("Deleting [" + entity + "]"));
}
compassGps.executeForMirror(new CompassCallbackWithoutResult() {
protected void doInCompassWithoutResult(CompassSession session) throws CompassException {
session.delete(entity);
}
});
} catch (Exception e) {
if (device.isIgnoreMirrorExceptions()) {
log.error(device.buildMessage("Failed while deleting [" + entity + "]"), e);
} else {
throw new HibernateGpsDeviceException(device.buildMessage("Failed while deleting [" + entity + "]"), e);
}
}
}
protected void doInsert(CompassSession session, PostInsertEvent postInsertEvent, Object entity, CompassGpsInterfaceDevice compassGps) {
if (marshallIds) {
Serializable id = postInsertEvent.getId();
postInsertEvent.getPersister().setIdentifier(entity, id, postInsertEvent.getSession().getEntityMode());
}
Collection<CollectionEntry> collectionsBefore = null;
if (processCollections) {
collectionsBefore = new HashSet<CollectionEntry>(postInsertEvent.getSession().getPersistenceContext().getCollectionEntries().values());
}
if (pendingCascades) {
HibernateEventListenerUtils.registerRemovalHook(postInsertEvent.getSession(), pendingCreate, entity);
Collection dependencies = HibernateEventListenerUtils.getUnpersistedCascades(compassGps, entity,
(SessionFactoryImplementor) device.getSessionFactory(), Cascade.CREATE, new HashSet());
if (!dependencies.isEmpty()) {
pendingCreate.put(entity, dependencies);
} else {
dependencies = HibernateEventListenerUtils.getAssociatedDependencies(entity, pendingCreate);
if (!dependencies.isEmpty()) {
pendingCreate.put(entity, dependencies);
} else {
session.create(entity);
}
}
HibernateEventListenerUtils.persistPending(session, entity, pendingCreate, true);
} else {
session.create(entity);
}
if (processCollections) {
Collection<CollectionEntry> collectionsAfter = postInsertEvent.getSession().getPersistenceContext().getCollectionEntries().values();
for (CollectionEntry collection : collectionsAfter) {
if (!collectionsBefore.contains(collection)) {
collection.setProcessed(true);
}
}
}
}
protected void doUpdate(CompassSession session, CompassGpsInterfaceDevice compassGps, Object entity, EventSource eventSource) {
if (pendingCascades) {
HibernateEventListenerUtils.registerRemovalHook(eventSource, pendingSave, entity);
Collection dependencies = HibernateEventListenerUtils.getUnpersistedCascades(compassGps, entity,
(SessionFactoryImplementor) device.getSessionFactory(), Cascade.SAVE, new HashSet());
if (!dependencies.isEmpty()) {
pendingSave.put(entity, dependencies);
} else {
dependencies = HibernateEventListenerUtils.getAssociatedDependencies(entity, pendingSave);
if (!dependencies.isEmpty()) {
pendingSave.put(entity, dependencies);
} else {
session.save(entity);
}
}
HibernateEventListenerUtils.persistPending(session, entity, pendingSave, false);
} else {
session.save(entity);
}
}
}