001: //$Id: DefaultReplicateEventListener.java 11088 2007-01-24 14:29:43Z max.andersen@jboss.com $
002: package org.hibernate.event.def;
003:
004: import org.hibernate.HibernateException;
005: import org.hibernate.TransientObjectException;
006: import org.hibernate.ReplicationMode;
007: import org.hibernate.LockMode;
008: import org.hibernate.engine.Cascade;
009: import org.hibernate.engine.CascadingAction;
010: import org.hibernate.engine.EntityKey;
011: import org.hibernate.engine.Status;
012: import org.hibernate.event.EventSource;
013: import org.hibernate.event.ReplicateEvent;
014: import org.hibernate.event.ReplicateEventListener;
015: import org.hibernate.engine.SessionImplementor;
016: import org.hibernate.persister.entity.EntityPersister;
017: import org.hibernate.pretty.MessageHelper;
018: import org.hibernate.type.Type;
019:
020: import java.io.Serializable;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024:
025: /**
026: * Defines the default replicate event listener used by Hibernate to replicate
027: * entities in response to generated replicate events.
028: *
029: * @author Steve Ebersole
030: */
031: public class DefaultReplicateEventListener extends
032: AbstractSaveEventListener implements ReplicateEventListener {
033:
034: private static final Log log = LogFactory
035: .getLog(DefaultReplicateEventListener.class);
036:
037: /**
038: * Handle the given replicate event.
039: *
040: * @param event The replicate event to be handled.
041: *
042: * @throws TransientObjectException An invalid attempt to replicate a transient entity.
043: */
044: public void onReplicate(ReplicateEvent event) {
045: final EventSource source = event.getSession();
046: if (source.getPersistenceContext()
047: .reassociateIfUninitializedProxy(event.getObject())) {
048: log.trace("uninitialized proxy passed to replicate()");
049: return;
050: }
051:
052: Object entity = source.getPersistenceContext()
053: .unproxyAndReassociate(event.getObject());
054:
055: if (source.getPersistenceContext().isEntryFor(entity)) {
056: log
057: .trace("ignoring persistent instance passed to replicate()");
058: //hum ... should we cascade anyway? throw an exception? fine like it is?
059: return;
060: }
061:
062: EntityPersister persister = source.getEntityPersister(event
063: .getEntityName(), entity);
064:
065: // get the id from the object
066: /*if ( persister.isUnsaved(entity, source) ) {
067: throw new TransientObjectException("transient instance passed to replicate()");
068: }*/
069: Serializable id = persister.getIdentifier(entity, source
070: .getEntityMode());
071: if (id == null) {
072: throw new TransientObjectException(
073: "instance with null id passed to replicate()");
074: }
075:
076: final ReplicationMode replicationMode = event
077: .getReplicationMode();
078:
079: final Object oldVersion;
080: if (replicationMode == ReplicationMode.EXCEPTION) {
081: //always do an INSERT, and let it fail by constraint violation
082: oldVersion = null;
083: } else {
084: //what is the version on the database?
085: oldVersion = persister.getCurrentVersion(id, source);
086: }
087:
088: if (oldVersion != null) {
089: if (log.isTraceEnabled()) {
090: log.trace("found existing row for "
091: + MessageHelper.infoString(persister, id,
092: source.getFactory()));
093: }
094:
095: /// HHH-2378
096: final Object realOldVersion = persister.isVersioned() ? oldVersion
097: : null;
098:
099: boolean canReplicate = replicationMode
100: .shouldOverwriteCurrentVersion(entity,
101: realOldVersion, persister.getVersion(
102: entity, source.getEntityMode()),
103: persister.getVersionType());
104:
105: if (canReplicate) {
106: //will result in a SQL UPDATE:
107: performReplication(entity, id, realOldVersion,
108: persister, replicationMode, source);
109: } else {
110: //else do nothing (don't even reassociate object!)
111: log.trace("no need to replicate");
112: }
113:
114: //TODO: would it be better to do a refresh from db?
115: } else {
116: // no existing row - do an insert
117: if (log.isTraceEnabled()) {
118: log.trace("no existing row, replicating new instance "
119: + MessageHelper.infoString(persister, id,
120: source.getFactory()));
121: }
122:
123: final boolean regenerate = persister
124: .isIdentifierAssignedByInsert(); // prefer re-generation of identity!
125: final EntityKey key = regenerate ? null : new EntityKey(id,
126: persister, source.getEntityMode());
127:
128: performSaveOrReplicate(entity, key, persister, regenerate,
129: replicationMode, source, true);
130:
131: }
132: }
133:
134: protected boolean visitCollectionsBeforeSave(Object entity,
135: Serializable id, Object[] values, Type[] types,
136: EventSource source) {
137: //TODO: we use two visitors here, inefficient!
138: OnReplicateVisitor visitor = new OnReplicateVisitor(source, id,
139: entity, false);
140: visitor.processEntityPropertyValues(values, types);
141: return super .visitCollectionsBeforeSave(entity, id, values,
142: types, source);
143: }
144:
145: protected boolean substituteValuesIfNecessary(Object entity,
146: Serializable id, Object[] values,
147: EntityPersister persister, SessionImplementor source) {
148: return false;
149: }
150:
151: protected boolean isVersionIncrementDisabled() {
152: return true;
153: }
154:
155: private void performReplication(Object entity, Serializable id,
156: Object version, EntityPersister persister,
157: ReplicationMode replicationMode, EventSource source)
158: throws HibernateException {
159:
160: if (log.isTraceEnabled()) {
161: log.trace("replicating changes to "
162: + MessageHelper.infoString(persister, id, source
163: .getFactory()));
164: }
165:
166: new OnReplicateVisitor(source, id, entity, true).process(
167: entity, persister);
168:
169: source.getPersistenceContext().addEntity(entity,
170: Status.MANAGED, null,
171: new EntityKey(id, persister, source.getEntityMode()),
172: version, LockMode.NONE, true, persister, true, false);
173:
174: cascadeAfterReplicate(entity, persister, replicationMode,
175: source);
176: }
177:
178: private void cascadeAfterReplicate(Object entity,
179: EntityPersister persister, ReplicationMode replicationMode,
180: EventSource source) {
181: source.getPersistenceContext().incrementCascadeLevel();
182: try {
183: new Cascade(CascadingAction.REPLICATE,
184: Cascade.AFTER_UPDATE, source).cascade(persister,
185: entity, replicationMode);
186: } finally {
187: source.getPersistenceContext().decrementCascadeLevel();
188: }
189: }
190:
191: protected CascadingAction getCascadeAction() {
192: return CascadingAction.REPLICATE;
193: }
194: }
|