//
// ERCNSnapshot.java
// Project ERChangeNotificationJMS
//
// Created by tatsuya on Sat Aug 31 2002
//
package er.changenotification;
import java.io.Serializable;
import java.util.Enumeration;
import com.webobjects.appserver.WOApplication;
import com.webobjects.eoaccess.EODatabaseContext;
import com.webobjects.eocontrol.EOEditingContext;
import com.webobjects.eocontrol.EOKeyGlobalID;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableDictionary;
import com.webobjects.foundation.NSNotification;
/**
* ERCNSnapshot encapsulates changes in enterprise objects for single
* saveChanges operation. It implements java.io.Serializable interface so that
* it can be transmitted between application instances as a JMS object message.
* <p>
* Its constructor is called by ERCNPublisher object. It processes the
* change notification posted by the default EOObjectStoreCoordinator and
* populate the dictionaries with the snapshots of updated enterprise objects.
*/
public class ERCNSnapshot implements Serializable {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
public static final String INSERTED = "inserted";
public static final String UPDATED = "updated";
public static final String DELETED = "deleted";
private final String _senderHost;
private final Number _senderPort;
private final String _senderAppName;
private final NSDictionary _shapshotsForInsertionGroupedByEntity;
private final NSDictionary _shapshotsForUpdateGroupedByEntity;
private final NSDictionary _globalIDsForDeletionGroupedByEntity;
private transient String _toString;
private transient int _entryCount = 0;
public ERCNSnapshot(NSNotification notification) {
WOApplication app = WOApplication.application();
_senderHost = app.host();
_senderPort = app.port(); // Don't forget to apply Max's change
_senderAppName = app.name();
NSDictionary userInfo = notification.userInfo();
ERCNConfiguration configuration = ERCNNotificationCoordinator.coordinator().configuration();
if (configuration.changeTypesToPublish().containsObject(INSERTED))
_shapshotsForInsertionGroupedByEntity = snapshotsGroupedByEntity((NSArray)userInfo.objectForKey("inserted"));
else
_shapshotsForInsertionGroupedByEntity = NSDictionary.EmptyDictionary;
if (configuration.changeTypesToPublish().containsObject(UPDATED))
_shapshotsForUpdateGroupedByEntity = snapshotsGroupedByEntity((NSArray)userInfo.objectForKey("updated"));
else
_shapshotsForUpdateGroupedByEntity = NSDictionary.EmptyDictionary;
if (configuration.changeTypesToPublish().containsObject(DELETED))
_globalIDsForDeletionGroupedByEntity = globalIDsGroupedByEntity((NSArray)userInfo.objectForKey("deleted"));
else
_globalIDsForDeletionGroupedByEntity = NSDictionary.EmptyDictionary;
}
public NSDictionary shapshotsForInsertionGroupedByEntity() {
return _shapshotsForInsertionGroupedByEntity;
}
public NSDictionary shapshotsForUpdateGroupedByEntity() {
return _shapshotsForUpdateGroupedByEntity;
}
public NSDictionary globalIDsForDeletionGroupedByEntity() {
return _globalIDsForDeletionGroupedByEntity;
}
public boolean shouldPostChange() {
return _entryCount > 0;
}
public static boolean shouldApplyChangeFor(String operation) {
ERCNConfiguration configuration = ERCNNotificationCoordinator.coordinator().configuration();
return configuration.changeTypesToSubscribe().containsObject(operation);
}
public static boolean shouldSynchronizeEntity(String entityName) {
ERCNConfiguration configuration = ERCNNotificationCoordinator.coordinator().configuration();
return ! configuration.entitiesNotToSynchronize().containsObject(entityName);
}
public String senderHost() {
return _senderHost;
}
public Number senderPort() {
return _senderPort;
}
public String senderAppName() {
return _senderAppName;
}
public NSDictionary snapshotsGroupedByEntity(NSArray objects) {
if (objects == null) return NSDictionary.EmptyDictionary;
NSMutableDictionary result = new NSMutableDictionary();
EOEditingContext ec = new EOEditingContext();
ec.lock();
Enumeration e = objects.objectEnumerator();
while (e.hasMoreElements()) {
EOKeyGlobalID globalID = (EOKeyGlobalID) e.nextElement();
String entityName = globalID.entityName();
if (shouldSynchronizeEntity(entityName)) {
EODatabaseContext dbContext = ERCNNotificationCoordinator.databaseContextForEntityNamed(entityName, ec);
NSMutableArray snapshotsForEntity = (NSMutableArray)result.objectForKey(entityName);
if (snapshotsForEntity == null) {
snapshotsForEntity = new NSMutableArray();
result.setObjectForKey(snapshotsForEntity, entityName);
}
NSDictionary snapshot = dbContext.snapshotForGlobalID(globalID);
if (snapshot != null) {
snapshotsForEntity.addObject(snapshot);
_entryCount++;
}
}
}
ec.unlock();
return result.immutableClone();
}
public NSDictionary globalIDsGroupedByEntity(NSArray objects) {
if (objects == null) return NSDictionary.EmptyDictionary;
NSMutableDictionary result = new NSMutableDictionary();
Enumeration e = objects.objectEnumerator();
while (e.hasMoreElements()) {
EOKeyGlobalID globalID = (EOKeyGlobalID) e.nextElement();
String entityName = globalID.entityName();
if (shouldSynchronizeEntity(entityName)) {
NSMutableArray globalIDsForEntity = (NSMutableArray)result.objectForKey(entityName);
if (globalIDsForEntity == null) {
globalIDsForEntity = new NSMutableArray();
result.setObjectForKey(globalIDsForEntity, entityName);
}
globalIDsForEntity.addObject(globalID);
_entryCount++;
}
}
return result.immutableClone();
}
@Override
public String toString() {
if (_toString == null) {
StringBuilder sbuf = new StringBuilder();
sbuf.append('<').append(getClass().getName()).append('\n');
sbuf.append(" sender: ").append(senderHost()).append(':')
.append(senderPort()).append('/').append(senderAppName()).append('\n');
sbuf.append(" insertion: ").append(_summaryForChangeType(_shapshotsForInsertionGroupedByEntity));
sbuf.append(" update: ").append(_summaryForChangeType(_shapshotsForUpdateGroupedByEntity));
sbuf.append(" deletion: ").append(_summaryForChangeType(_globalIDsForDeletionGroupedByEntity));
sbuf.append('>');
_toString = sbuf.toString();
}
return _toString;
}
private String _summaryForChangeType(NSDictionary objectsGroupedByEntity) {
StringBuilder sbuf = new StringBuilder();
if (objectsGroupedByEntity.allKeys().count() == 0) {
sbuf.append("none \n");
} else {
sbuf.append('\n');
Enumeration entityNames = objectsGroupedByEntity.keyEnumerator();
while (entityNames.hasMoreElements()) {
String entityName = (String)entityNames.nextElement();
sbuf.append(" ").append(entityName).append(": ");
NSArray objects = (NSArray)objectsGroupedByEntity.objectForKey(entityName);
sbuf.append(objects.count()).append(" objects \n");
}
}
return sbuf.toString();
}
}