Last modified by Kieran Kelleher on 2010/09/08 20:09

Show last authors
1 |=(((
2 Contents
3 )))
4 |(((
5 {{toc minLevel="2" style="disc"/}}
6 )))
7
8 == Overview ==
9
10 Errors in locking your editing context (shared editing contexts or not) will cause your instance to deadlock. This is one of the most vexing problems.
11
12 //Do you have to lock & unlock the editing context only when you are using a stateless section (directActions) of your application or do you have to lock & unlock it in state as well? What about the defaultEditingContext, does that need locking & unlocking too?//
13
14 If your sessions only use the default editing context, you don't worry about locking.
15
16 //Also, manually locking/unlocking objects is error prone. And the resulting errors are the most difficult to fix since they only occur randomly on heavy load. I really feel that WebObjects should use Java synchronization to ensure safe access without requiring manual coding. But this would probably mean a considerable rework of WebObjects//
17
18 I used to think along the same lines, but it turns out this would introduce one hell-uv-a-lot of overhead. You'd have a performance situation not unlike the original Java collections (BAD). If you can acquire one lock and then plow through EC and EO API at will until unlock, you really save some cycles. I'm sure there are others more enlightened here that can elaborate or correct me here; but this is my impression.
19
20 BTW: I tend to create extra ECs at the Session level and do the locking and unlocking in the Session awake and sleep. The request handler prevents more than one request from getting access to a Session at a time; and you are guaranteed that Session awake and sleep trigger just once per request cycle. That's not true of component awake and sleep calls. You have a greater potential for a deadlock if you do the lock/unlock at the component level:
21
22 Transaction starts on one page (lock), then instead of returning that page after action, you create another page (potential lock again, deadlock).
23
24 The only tricky thing I've run into is if your app uses Long Running Request Page. That's a case where you get multiple request/responses as viewed by the session over one logical "request". That is, the status page will be reloading, but your background job is busy doing its thing continuously. If it's busy using a EC that is locking and unlocking at the session awake/sleep you wouldn't get very far without complaints. So in these cases, if a user enters such a page AND is using a EC, I disable the automatic session lock/unlock for that EC until the job is over. Or I create a EC used strictly for the background job.
25
26 === David LeBer ===
27
28 In general you are better to lock EC's in the session (if you've got one) rather than the component.
29
30 As that is the usual approach, there are a couple of solutions to implement that for you:
31
32 * [[MultiECLockManager>>url:http://wocode.com/cgi-bin/WebObjects/WOCode.woa/1/wa/ShareCodeItem?itemId=301||shape="rect"]]
33 * [[Project Wonder's ERXEC>>url:http://wonder.sourceforge.net/javadoc/er/extensions/ERXEC.html||shape="rect"]]
34
35 === Jonathan Rochkind ===
36
37 Note: One very handy (if I do say so myself) reusable solution for locking ECs you create (not neccesary for session default EC) can be found at: [[http:~~/~~/WOCode.com/cgi-bin/WebObjects/WOCode.woa/wa/ShareCodeItem?itemId=301>>url:http://WOCode.com/cgi-bin/WebObjects/WOCode.woa/wa/ShareCodeItem?itemId=301||shape="rect"]]
38
39 === Anjo Krank ===
40
41 One thing to watch out for is that EC locking on the page's awake() and sleep() doesn't really work, because awake may be called more often than sleep. This means that normally you can't use DirectToWeb because it does just that. However, ProjectWonder has an editing context factory, an automatically locking and unlocking EC subclass and a lock manager that will relaese all locks when the application sleeps.
42
43 This works nicely with long response pages, provided that you don't use the session's editing context in the long running task.
44
45 == Locking in a DirectAction ==
46
47 //If i call session().defaultEditingContext() from within a DirectAction, I'm currently defensively locking it. Do I need to, or does it follow the same (automatic) locking rules as in a normal WOComponent?//
48
49 === Anjo Krank ===
50
51 You don't need to lock it.
52
53 But anyway, doing sth like:
54
55 {{code}}
56
57 public WOActionResults someAction() {
58 WOActionResults page = pageWithName("Foo");
59 session().defaultEditingContext().lock;
60 try {
61 ...
62 } finally {
63 session().defaultEditingContext().unlock;
64 }
65 return page;
66 }
67
68 {{/code}}
69
70 isn't sufficient, as the appendToResponse phase only occurs **after** the page is returned and faults could still get fired there... so when you create an EC there instead of using the session EC, you still have to lock it in the component.
71
72 === Chuck Hill ===
73
74 Here is another way to handle this:
75
76 {{code}}
77
78 public WOActionResults someAction() {
79 WOComponent page = pageWithName("Foo");
80 WOResponse response;
81 session().defaultEditingContext().lock;
82 try {
83 ...
84 response = page.generateResponse().
85 } finally {
86 session().defaultEditingContext().unlock;
87 }
88 return response;
89 }
90
91 {{/code}}
92
93 The call to generateResponse before the unlock ensures that appendToResponse is called while the EC is locked and that all the faults are fired in a safe state.
94
95 === Robert Walker ===
96
97 I generally avoid using the session's default editing context within direct actions. A call to session().defaultEditingContext() will create a session, even if you don't actually need one. It can be more efficient to use a new editing context and lock it as necessary, since this will not cause unnecessary sessions to be created.
98
99 == Tracking EOEditingContext Locks ==
100
101 Project WOnder's ERXEC provides extensive logging facilities for tracking EOEditingContext locks. If you are not interested in using Project WOnder, there are several other ways:
102
103 You can display the stack trace of editingcontext locking warnings with the Launch Argument:
104
105 {{noformat}}
106
107 -NSDebugLevel 2 -NSDebugGroups 18
108
109 {{/noformat}}
110
111 This will give you some information but there is a better way. You need to sub-class EOEditingContext like this:
112
113 === LockErrorScreamerEditingContext ===
114
115 {{code}}
116
117 // LockErrorScreamerEditingContext.java
118 //
119 // Copyright (c) 2002-2003 Red Shed Software. All rights reserved.
120 // by Jonathan 'Wolf' Rentzsch (jon at redshed dot net)
121 // enhanced by Anthony Ingraldi (a.m.ingraldi at larc.nasa.gov)
122 //
123 // Thu Mar 28 2002 wolf: Created.
124 // Thu Apr 04 2002 wolf: Made NSRecursiveLock-aware by Anthony.
125 // Thu Jun 22 2003 wolf: Made finalizer-aware. Thanks to Chuck Hill.
126
127 import com.webobjects.eocontrol.*;
128 import com.webobjects.foundation.*;
129 import java.io.StringWriter;
130 import java.io.PrintWriter;
131
132 public class LockErrorScreamerEditingContext extends EOEditingContext {
133 private String _nameOfLockingThread = null;
134 private NSMutableArray _stackTraces = new NSMutableArray();
135
136 public LockErrorScreamerEditingContext() {
137 super();
138 }
139
140 public LockErrorScreamerEditingContext(EOObjectStore parent) {
141 super(parent);
142 }
143
144 public void lock() {
145 String nameOfCurrentThread = Thread.currentThread().getName();
146 if (_stackTraces.count() == 0) {
147 _stackTraces.addObject(_trace());
148 _nameOfLockingThread = nameOfCurrentThread;
149 //NSLog.err.appendln("+++ Lock number (" + _stackTraces.count() + ") in " + nameOfCurrentThread);
150 } else {
151 if (nameOfCurrentThread.equals(_nameOfLockingThread)) {
152 _stackTraces.addObject(_trace());
153 //NSLog.err.appendln("+++ Lock number (" + _stackTraces.count() + ") in " + nameOfCurrentThread);
154 } else {
155 NSLog.err.appendln("!!! Attempting to lock editing context from " + nameOfCurrentThread
156 + " that was previously locked in " + _nameOfLockingThread);
157 NSLog.err.appendln("!!! Current stack trace: \n" + _trace());
158 NSLog.err.appendln("!!! Stack trace for most recent lock: \n" + _stackTraces.lastObject());
159 }
160 }
161 super.lock();
162 }
163
164 public void unlock() {
165 super.unlock();
166 if (_stackTraces.count() > 0)
167 _stackTraces.removeLastObject();
168 if (_stackTraces.count() == 0)
169 _nameOfLockingThread = null;
170 String nameOfCurrentThread = Thread.currentThread().getName();
171 //NSLog.err.appendln("--- Unlocked in " + nameOfCurrentThread + " (" + _stackTraces.count() + " remaining)");
172 }
173
174 public void goodbye() {
175 if (_stackTraces.count() != 0) {
176 NSLog.err.appendln("!!! editing context being disposed with " + _stackTraces.count() + " locks.");
177 NSLog.err.appendln("!!! Most recently locked by: \n"
178 + _stackTraces.lastObject());
179 }
180 }
181
182 public void dispose() {
183 goodbye();
184 super.dispose();
185 }
186
187 protected void finalize() throws Throwable {
188 try {
189 goodbye();
190 } finally {
191 super.finalize();
192 }
193 }
194
195 private String _trace() {
196 StringWriter stringWriter = new StringWriter();
197 PrintWriter printWriter = new PrintWriter(stringWriter);
198 (new Throwable()).printStackTrace(printWriter);
199 return stringWriter.toString();
200 }
201 }
202
203 {{/code}}
204
205 Now you'll have to get the subclass substituted whenever an EOEditingContext is created. Too bad there's no generalized EOEditingContext factory method in WOF, but this will cover default editing contexts:
206
207 {{code}}
208
209 public class Session extends WOSession {
210 public Session() {
211 super();
212 setDefaultEditingContext( new LockErrorScreamerEditingContext() );
213 }
214
215 public Session( String sessionID ) {
216 super( sessionID );
217 setDefaultEditingContext( new LockErrorScreamerEditingContext() );
218 }
219
220 ...
221 }
222
223 {{/code}}
224
225 == EOObjectStoreCoordinator locking ==
226
227 Most developers do not need to concern themselves with EOObjectStoreCoordinator locking
228 but for those who are doing more than the usual EOF manipulation, it is advised to lock EOObjectStoreCoordinator in the following situations:
229
230 * When using methods of EOObjectStoreCoordinator directly
231 * When using methods of EOEntity, for example see ERXFetchSpecificationBatchIterator constructor, entity.schemabasedQualifier(...)
232
233 Note that locking an EOObjectStoreCoordinator will in turn lock each of its registered
234 EOCooperatingObjectStores, which are typically instances of the concrete EODatabaseContext
235 class.
236
237 == EODatabaseContext locking ==
238
239 Most developers do not need to concern themselves with EODatabaseContext locking
240 but for those who are doing more than the usual EOF manipulation, it is advised to lock EODatabaseContext in the following situations:
241
242 * When using methods of EODatabaseContext directly
243 * When using EOAdaptor
244 * When using EOAdaptorChannel
245 * When using EODatabase
246 * When using ERXSQLHelper
247
248 == EOF Core Classes Overview ==
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276 {{unmigrated-wiki-markup}}
277 !EOF-Classes-Guide-MS.pdf|attachment=EOF-Classes-Guide-MS.pdf,width=407,height=198!
278 {{/unmigrated-wiki-markup}}
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302 This is a useful diagram with overview of core classes and how they are related.