package er.extensions.eof; import java.io.Serializable; import java.util.concurrent.locks.ReentrantLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.webobjects.eoaccess.EOAdaptorChannel; import com.webobjects.eoaccess.EOAdaptorOperation; import com.webobjects.eoaccess.EODatabaseChannel; import com.webobjects.eoaccess.EODatabaseContext; import com.webobjects.eoaccess.EOEntity; import com.webobjects.eoaccess.EOGeneralAdaptorException; import com.webobjects.eoaccess.EOModelGroup; import com.webobjects.eoaccess.EOUtilities; import com.webobjects.eocontrol.EOEditingContext; import com.webobjects.eocontrol.EOQualifier; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSMutableArray; import com.webobjects.foundation.NSNotificationCenter; import er.extensions.foundation.ERXProperties; /** * This class is a wrapper for the EOAdaptorOperation class * in order to serialized the information from an EOAdaptorOperation to files. * It can used for example by ERXDatabaseContextDelegate to serialize * operations to disk, useful for cross database replications. * * @author david@cluster9.com * @author ak moved stuff from ERXEOAccessUtilities to here */ public class ERXAdaptorOperationWrapper 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; private static final Logger log = LoggerFactory.getLogger(ERXAdaptorOperationWrapper.class); public static final ReentrantLock adaptorOperationsLock = new ReentrantLock(); public static final String AdaptorOperationsDidPerformNotification = "AdaptorOperationsDidPerform"; private static Boolean postAdaptorOperationNotifications = null; transient EOAdaptorOperation operation; String entityName; private int operator; private NSArray attributes; private NSDictionary changedValues; private EOQualifier qualifier; public ERXAdaptorOperationWrapper(EOAdaptorOperation aop) { super(); operation = aop; entityName = aop.entity().name(); operator = aop.adaptorOperator(); attributes = aop.attributes(); changedValues = aop.changedValues(); qualifier = aop.qualifier(); } public EOAdaptorOperation operation() { if (operation == null) { EOEntity entity = EOModelGroup.defaultGroup().entityNamed(entityName); operation = new EOAdaptorOperation(entity); operation.setAdaptorOperator(operator); operation.setAttributes(attributes); operation.setChangedValues(changedValues); operation.setQualifier(qualifier); } return operation; } /** * Returns an array of ERXAdaptorOperations that can be serialzed to the transport of your choice. * @param adaptorOps */ public static NSArray wrappedAdaptorOperations(NSArray adaptorOps) { adaptorOperationsLock.lock(); try { NSMutableArray ops = new NSMutableArray(); if(adaptorOps.count() > 0) { for (int i = 0; i < adaptorOps.count(); i++) { EOAdaptorOperation a = (EOAdaptorOperation) adaptorOps.objectAtIndex(i); ERXAdaptorOperationWrapper wrapper = new ERXAdaptorOperationWrapper(a); ops.addObject(wrapper); } } return ops; } finally { adaptorOperationsLock.unlock(); } } /** * Unwraps and performs the supplied adaptor operations. * @param ops */ public static void performWrappedAdaptorOperations(NSArray ops) { EOEditingContext ec = ERXEC.newEditingContext(); ec.lock(); try { // FIXME use the entityName information from each EOAdaptorOperation to get the correct // database context, this implementation here only works if all EOModels use the same database ERXAdaptorOperationWrapper op = (ERXAdaptorOperationWrapper) ops.lastObject(); EODatabaseContext context = EOUtilities.databaseContextForModelNamed(ec, op.operation().entity().model().name()); context.lock(); adaptorOperationsLock.lock(); try { EODatabaseChannel dchannel = context.availableChannel(); EOAdaptorChannel achannel = dchannel.adaptorChannel(); achannel.adaptorContext().beginTransaction(); boolean wasOpen = achannel.isOpen(); if (!wasOpen) { achannel.openChannel(); } for(int i = 0; i < ops.count(); i++) { op = (ERXAdaptorOperationWrapper)ops.objectAtIndex(i); try { achannel.performAdaptorOperation(op.operation()); } catch(EOGeneralAdaptorException ex) { log.error("Failed op {}: {}\n{}", i, ex, op); throw ex; } } achannel.adaptorContext().commitTransaction(); if (!wasOpen) { achannel.closeChannel(); } } finally { adaptorOperationsLock.unlock(); context.unlock(); } } finally { ec.unlock(); } } public static void adaptorOperationsDidPerform(NSArray ops) { if (postAdaptorOperationNotifications() && ops.count() > 0) { NSNotificationCenter.defaultCenter().postNotification(AdaptorOperationsDidPerformNotification, ops); } } /** * @return <code>true</code> if the system property * <code>er.extensions.ERXDatabaseContextDelegate.postAdaptorOperationNotifications</code> * is set to true and if ERXThreadStorage.valueForKey(disabledForThreadKey) returns false or null, false otherwise. */ private static boolean postAdaptorOperationNotifications() { if (postAdaptorOperationNotifications == null) { postAdaptorOperationNotifications = ERXProperties.booleanForKeyWithDefault( "er.extensions.ERXAdaptorationWrapper.postAdaptorOperationNotifications", false) ? Boolean.TRUE : Boolean.FALSE; } return postAdaptorOperationNotifications.booleanValue(); } }