Source code: er/extensions/ERXDefaultEditingContextDelegate.java
1 /*
2 * Copyright (C) NetStruxr, Inc. All rights reserved.
3 *
4 * This software is published under the terms of the NetStruxr
5 * Public Software License version 0.5, a copy of which has been
6 * included with this distribution in the LICENSE.NPL file. */
7 package er.extensions;
8
9 import com.webobjects.foundation.*;
10 import com.webobjects.eocontrol.*;
11 import com.webobjects.eoaccess.*;
12 import com.webobjects.appserver.*;
13 import java.util.*;
14
15 /**
16 * Default editing context delegate. This delegate
17 * augments the regular transaction process by adding
18 * the calling of willInsert, willUpdate or willDelete
19 * on enterprise objects that are of type ERXGenericRecord
20 * after saveChanges is called on the editing context, but
21 * before validateForSave is called on the object. These
22 * methods can give the object a last chance to modify itself
23 * before validation occurs. The second enhancement is a built
24 * in flushing of caches on subclasses of ERXGenericRecords
25 * when objects have changes merged in or are invalidated.
26 * Being able to maintain caches on enterprise objects that
27 * are flushed when the underlying values change can be very
28 * handy.
29 */
30 public class ERXDefaultEditingContextDelegate extends ERXEditingContextDelegate {
31
32 /** logging support */
33 public static final ERXLogger log = ERXLogger.getERXLogger(ERXDefaultEditingContextDelegate.class);
34 /** logging support for modified objects */
35 public static final ERXLogger logMod = ERXLogger.getERXLogger("er.transaction.delegate.EREditingContextDelegate.modifedObjects");
36
37 /**
38 * flag that can tell if the editing context is in
39 * the middle of a will save changes call.
40 */
41 // FIXME inherently single threaded
42 private boolean _isInWillSaveChanges=false;
43 /**
44 * Can tell if the delegate is in the middle of a
45 * call to will save changes.
46 * @return if the delegate is already processing
47 * a request at the time when another request
48 * comes in.
49 */
50 public boolean isInWillSaveChanges() { return _isInWillSaveChanges; }
51
52 /**
53 * Enumerates through all of the objects that have been
54 * changed, inserted and deleted calling the appropriate
55 * will* method, willInsert, etc. on each of the objects
56 * if they are of type ERXGenericRecord. Note that this
57 * method is called before validateForSave is called on
58 * any of the objects.
59 * @param ec editing context that is about to be saved.
60 */
61 public void editingContextWillSaveChanges(EOEditingContext ec) throws Throwable {
62 try {
63 if (log.isDebugEnabled()) log.debug("EditingContextWillSaveChanges: start calling will*");
64 _isInWillSaveChanges=true;
65 if (ec != null && ec.hasChanges()) {
66 NSNotificationCenter.defaultCenter().postNotification(ERXExtensions.objectsWillChangeInEditingContext, ec);
67 // Changed objects
68 if (ec.updatedObjects()!=null && ec.updatedObjects().count()>0) {
69 for (Enumeration e = ec.updatedObjects().objectEnumerator(); e.hasMoreElements();) {
70 EOEnterpriseObject eo = (EOEnterpriseObject)e.nextElement();
71 if (eo instanceof ERXGenericRecord)
72 ((ERXGenericRecord)eo).willUpdate();
73 }
74 }
75 // Deleted objects
76 if (ec.deletedObjects()!=null && ec.deletedObjects().count()>0) {
77 for (Enumeration e = ec.deletedObjects().objectEnumerator(); e.hasMoreElements();) {
78 EOEnterpriseObject eo = (EOEnterpriseObject)e.nextElement();
79 if (eo instanceof ERXGenericRecord)
80 ((ERXGenericRecord)eo).willDelete();
81 }
82 }
83 // Inserted objects
84 if (ec.insertedObjects()!=null && ec.insertedObjects().count()>0) {
85 for (Enumeration e = ec.insertedObjects().objectEnumerator(); e.hasMoreElements();) {
86 EOEnterpriseObject eo = (EOEnterpriseObject)e.nextElement();
87 if (eo instanceof ERXGenericRecord && !ec.deletedObjects().containsObject(eo))
88 ((ERXGenericRecord)eo).willInsert();
89 }
90 }
91 if (log.isDebugEnabled()) log.debug("EditingContextWillSaveChanges: done calling will*");
92 if (logMod.isDebugEnabled()) {
93 if (ec.updatedObjects()!=null) logMod.debug("** Updated Objects "+ec.updatedObjects().count()+" - "+ec.updatedObjects());
94 if (ec.insertedObjects()!=null) logMod.debug("** Inserted Objects "+ec.insertedObjects().count()+" - "+ec.insertedObjects());
95 if (ec.deletedObjects()!=null) logMod.debug("** Deleted Objects "+ec.deletedObjects().count()+" - "+ec.deletedObjects());
96 }
97 }
98 } catch (Throwable e) {
99 if (log.isDebugEnabled()) // i want this stack trace shown in one of the two categories, but not both
100 log.debug("Stack Trace:\n" + ERXUtilities.stackTrace(e));
101 else if (logMod.isDebugEnabled())
102 logMod.debug("Stack Trace:\n" + ERXUtilities.stackTrace(e));
103 try {
104 StringBuffer buffer = new StringBuffer();
105 buffer.append("The following exception has occurred with ec: " + ec + "\n" + ERXUtilities.stackTrace(e) + "\n");
106 if (ec.updatedObjects()!=null) {
107 buffer.append("** Updated Objects "+ec.updatedObjects().count());
108 for (Enumeration en = ec.updatedObjects().objectEnumerator(); en.hasMoreElements();) {
109 buffer.append('\n');
110 buffer.append(toDebugString((EOEnterpriseObject)en.nextElement()));
111 }
112 }
113 if (ec.insertedObjects()!=null) {
114 buffer.append("\n** Inserted Objects "+ec.insertedObjects().count());
115 for (Enumeration en = ec.insertedObjects().objectEnumerator(); en.hasMoreElements();) {
116 buffer.append('\n');
117 buffer.append(toDebugString((EOEnterpriseObject)en.nextElement()));
118 }
119 }
120 if (ec.deletedObjects()!=null) {
121 buffer.append("\n** Deleted Objects "+ec.deletedObjects().count());
122 for (Enumeration en = ec.deletedObjects().objectEnumerator(); en.hasMoreElements();) {
123 buffer.append('\n');
124 buffer.append(toDebugString((EOEnterpriseObject)en.nextElement()));
125 }
126 }
127 logMod.error(buffer);
128 } catch (Throwable e2) {
129 log.error("Caught "+e2+" trying to print editing context state");
130 } finally {
131 _isInWillSaveChanges=false;
132 }
133 throw e;
134 } finally {
135 _isInWillSaveChanges=false;
136 }
137 }
138
139 /**
140 * Returns a string of a verbose look a the given
141 * enterprise object. Also includes the primary key.
142 * @param eo enterprise object to create the debug string
143 * for.
144 * @return verbose description of the object.
145 */
146 // MOVEME: If this is usedful then it might be worth putting in EOGenericRecordClazz or ERXEOFUtilities
147 private static String toDebugString(EOEnterpriseObject eo) {
148 String result=null;
149 if (eo!=null) {
150 if (eo instanceof ERXGenericRecord) {
151 ERXGenericRecord rec = (ERXGenericRecord)eo;
152 result="PKey: " + rec.primaryKey() + " - " + rec.toLongString();
153 } else {
154 result="Pkey: "+EOUtilities.primaryKeyForObject(eo.editingContext(),eo)+" - "+eo;
155 }
156 }
157 return result;
158 }
159 /**
160 * When invalidating an object their local
161 * cache is flushed by calling the method: <code>
162 * flushCaches</code> on the enterprise object if
163 * it is an instance of ERXGenericRecord.
164 * @param anEditingContext current editing context
165 * @param anObject enterprise object to be invlidated
166 * @param anEOGlobalID global id to be invalidated
167 * @return true
168 */
169 public boolean editingContextShouldInvalidateObject(EOEditingContext anEOEditingContext,
170 EOEnterpriseObject anObject,
171 EOGlobalID anEOGlobalID) {
172 if (anObject instanceof ERXGenericRecord) {
173 ((ERXGenericRecord)anObject).flushCaches();
174 }
175 return true;
176 }
177
178 /**
179 * When merging changes into an object their local
180 * cache is flushed by calling the method: <code>
181 * flushCaches</code> on the enterprise object if
182 * it is an instance of ERXGenericRecord.
183 * @param anEditingContext current editing context
184 * @param object enterprise object to have changes
185 * merged into it.
186 * @return true
187 */
188 public boolean editingContextShouldMergeChangesForObject(EOEditingContext anEditingContext,
189 EOEnterpriseObject object) {
190 if (object instanceof ERXGenericRecord) {
191 ((ERXGenericRecord)object).flushCaches();
192 }
193 return true;
194 }
195 }