/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.aspectj.hibernate.stale.object.state;
import com.liferay.portal.kernel.model.BaseModel;
import com.liferay.portal.kernel.model.MVCCModel;
import com.liferay.portal.kernel.util.HashUtil;
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.SuppressAjWarnings;
import org.hibernate.HibernateException;
import org.hibernate.ObjectDeletedException;
import org.hibernate.StaleObjectStateException;
import org.hibernate.event.DeleteEvent;
import org.hibernate.event.MergeEvent;
import org.hibernate.event.SaveOrUpdateEvent;
/**
* @author Preston Crary
*/
@Aspect
@SuppressAjWarnings("adviceDidNotMatch")
public class HibernateStaleObjectStateAspect {
@AfterThrowing(
throwing = "sose",
value = "execution(void org.hibernate.event.def.DefaultMergeEventListener.onMerge(org.hibernate.event.MergeEvent)) && args(mergeEvent)"
)
public void suppressMergeFailureCause(
MergeEvent mergeEvent, StaleObjectStateException sose) {
_suppressFailureCause(mergeEvent.getOriginal(), sose);
}
@AfterThrowing(
throwing = "ode",
value = "execution(void org.hibernate.event.SaveOrUpdateEventListener.onSaveOrUpdate(org.hibernate.event.SaveOrUpdateEvent)) && args(saveOrUpdateEvent)"
)
public void suppressUpdateFailureCause(
SaveOrUpdateEvent saveOrUpdateEvent, ObjectDeletedException ode) {
_suppressFailureCause(saveOrUpdateEvent.getObject(), ode);
}
@AfterReturning(
"execution(void org.hibernate.event.DeleteEventListener.onDelete(" +
"org.hibernate.event.DeleteEvent)) && args(deleteEvent)"
)
public void trackDeleteEvent(DeleteEvent deleteEvent) {
_trackEvent("Delete", deleteEvent.getObject());
}
@AfterReturning(
"execution(void org.hibernate.event.def.DefaultMergeEventListener." +
"onMerge(org.hibernate.event.MergeEvent)) && args(mergeEvent)"
)
public void trackMergeEvent(MergeEvent mergeEvent) {
_trackEvent("Merge", mergeEvent.getOriginal());
}
@AfterReturning(
"execution(void org.hibernate.event.SaveOrUpdateEventListener." +
"onSaveOrUpdate(org.hibernate.event.SaveOrUpdateEvent)) &&" +
"args(saveOrUpdateEvent)"
)
public void trackSaveOrUpdateEvent(SaveOrUpdateEvent saveOrUpdateEvent) {
_trackEvent("SaveOrUpdate", saveOrUpdateEvent.getObject());
}
private void _suppressFailureCause(Object object, HibernateException he) {
if (!(object instanceof MVCCModel)) {
return;
}
he.addSuppressed(_events.get(new EventKey((BaseModel<?>)object)));
}
private void _trackEvent(String eventType, Object object) {
if (!(object instanceof MVCCModel)) {
return;
}
Exception exception = new Exception(
eventType + " record for " + object);
Exception previousException = _events.put(
new EventKey((BaseModel<?>)object), exception);
if (previousException != null) {
exception.addSuppressed(previousException);
}
}
private final Map<EventKey, Exception> _events = new ConcurrentHashMap<>();
private static class EventKey {
@Override
public boolean equals(Object obj) {
EventKey eventKey = (EventKey)obj;
if ((eventKey._mvccVersion == _mvccVersion) &&
Objects.equals(eventKey._primaryKey, _primaryKey) &&
Objects.equals(eventKey._modelClassName, _modelClassName)) {
return true;
}
return false;
}
@Override
public int hashCode() {
int hashCode = HashUtil.hash(0, _mvccVersion);
hashCode = HashUtil.hash(hashCode, _primaryKey);
hashCode = HashUtil.hash(hashCode, _modelClassName);
return hashCode;
}
private EventKey(BaseModel<?> baseModel) {
_modelClassName = baseModel.getModelClassName();
_primaryKey = baseModel.getPrimaryKeyObj();
MVCCModel mvccModel = (MVCCModel)baseModel;
_mvccVersion = mvccModel.getMvccVersion();
}
private final String _modelClassName;
private final long _mvccVersion;
private final Serializable _primaryKey;
}
}