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

Hide last authors
Kieran Kelleher 45.1 1 |=(((
2 Contents
3 )))
4 |(((
Kieran Kelleher 47.1 5 {{toc minLevel="2" style="disc"/}}
Kieran Kelleher 45.1 6 )))
Kieran Kelleher 15.1 7
Kieran Kelleher 29.1 8 == Overview ==
9
Kieran Kelleher 15.1 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
Kieran Kelleher 29.1 26 === David LeBer ===
Kieran Kelleher 15.1 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
Kieran Kelleher 45.1 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"]]
Kieran Kelleher 15.1 34
Kieran Kelleher 29.1 35 === Jonathan Rochkind ===
Kieran Kelleher 15.1 36
Kieran Kelleher 45.1 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"]]
Kieran Kelleher 15.1 38
Kieran Kelleher 29.1 39 === Anjo Krank ===
Kieran Kelleher 15.1 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
Kieran Kelleher 29.1 45 == Locking in a DirectAction ==
Kieran Kelleher 15.1 46
Kieran Kelleher 45.1 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?//
Kieran Kelleher 15.1 48
Kieran Kelleher 29.1 49 === Anjo Krank ===
Kieran Kelleher 15.1 50
51 You don't need to lock it.
52
53 But anyway, doing sth like:
54
Kieran Kelleher 29.1 55 {{code}}
Kieran Kelleher 15.1 56
Kieran Kelleher 29.1 57 public WOActionResults someAction() {
58 WOActionResults page = pageWithName("Foo");
59 session().defaultEditingContext().lock;
60 try {
61 ...
62 } finally {
63 session().defaultEditingContext().unlock;
Kieran Kelleher 15.1 64 }
Kieran Kelleher 29.1 65 return page;
66 }
Kieran Kelleher 15.1 67
Kieran Kelleher 29.1 68 {{/code}}
Kieran Kelleher 15.1 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
Kieran Kelleher 29.1 72 === Chuck Hill ===
Kieran Kelleher 15.1 73
74 Here is another way to handle this:
75
Kieran Kelleher 29.1 76 {{code}}
Kieran Kelleher 15.1 77
Kieran Kelleher 29.1 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;
Kieran Kelleher 15.1 87 }
Kieran Kelleher 29.1 88 return response;
89 }
Kieran Kelleher 15.1 90
Kieran Kelleher 29.1 91 {{/code}}
Kieran Kelleher 15.1 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
Kieran Kelleher 29.1 95 === Robert Walker ===
Kieran Kelleher 15.1 96
Kieran Kelleher 45.1 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.
Kieran Kelleher 15.1 98
Kieran Kelleher 29.1 99 == Tracking EOEditingContext Locks ==
Kieran Kelleher 15.1 100
Kieran Kelleher 45.1 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:
Kieran Kelleher 15.1 102
103 You can display the stack trace of editingcontext locking warnings with the Launch Argument:
104
Kieran Kelleher 29.1 105 {{noformat}}
Kieran Kelleher 15.1 106
107 -NSDebugLevel 2 -NSDebugGroups 18
108
Kieran Kelleher 29.1 109 {{/noformat}}
Kieran Kelleher 15.1 110
111 This will give you some information but there is a better way. You need to sub-class EOEditingContext like this:
112
Kieran Kelleher 29.1 113 === LockErrorScreamerEditingContext ===
Kieran Kelleher 15.1 114
Kieran Kelleher 29.1 115 {{code}}
Kieran Kelleher 15.1 116
Kieran Kelleher 29.1 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.
Kieran Kelleher 15.1 126
Kieran Kelleher 29.1 127 import com.webobjects.eocontrol.*;
128 import com.webobjects.foundation.*;
129 import java.io.StringWriter;
130 import java.io.PrintWriter;
Pascal Robert 21.1 131
Kieran Kelleher 29.1 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
Kieran Kelleher 15.1 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
Kieran Kelleher 29.1 207 {{code}}
Kieran Kelleher 15.1 208
Kieran Kelleher 29.1 209 public class Session extends WOSession {
210 public Session() {
211 super();
212 setDefaultEditingContext( new LockErrorScreamerEditingContext() );
213 }
Kieran Kelleher 15.1 214
Kieran Kelleher 29.1 215 public Session( String sessionID ) {
216 super( sessionID );
217 setDefaultEditingContext( new LockErrorScreamerEditingContext() );
218 }
Pascal Robert 21.1 219
Kieran Kelleher 29.1 220 ...
221 }
222
223 {{/code}}
Kieran Kelleher 39.1 224
225 == EOObjectStoreCoordinator locking ==
226
227 Most developers do not need to concern themselves with EOObjectStoreCoordinator locking
Kieran Kelleher 45.1 228 but for those who are doing more than the usual EOF manipulation, it is advised to lock EOObjectStoreCoordinator in the following situations:
Kieran Kelleher 39.1 229
230 * When using methods of EOObjectStoreCoordinator directly
Kieran Kelleher 43.1 231 * When using methods of EOEntity, for example see ERXFetchSpecificationBatchIterator constructor, entity.schemabasedQualifier(...)
Kieran Kelleher 39.1 232
233 Note that locking an EOObjectStoreCoordinator will in turn lock each of its registered
Kieran Kelleher 45.1 234 EOCooperatingObjectStores, which are typically instances of the concrete EODatabaseContext
235 class.
Kieran Kelleher 39.1 236
237 == EODatabaseContext locking ==
238
239 Most developers do not need to concern themselves with EODatabaseContext locking
Kieran Kelleher 45.1 240 but for those who are doing more than the usual EOF manipulation, it is advised to lock EODatabaseContext in the following situations:
Kieran Kelleher 39.1 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
Kieran Kelleher 45.1 250
251
252
253
254
255
256
257
258
259
Kieran Kelleher 46.1 260
261
Kieran Kelleher 48.1 262
263
Kieran Kelleher 49.1 264
265
Kieran Kelleher 50.1 266
267
Kieran Kelleher 51.1 268
269
Kieran Kelleher 52.1 270
271
Kieran Kelleher 53.1 272
273
Kieran Kelleher 54.1 274
275
Kieran Kelleher 45.1 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
Kieran Kelleher 46.1 286
287
Kieran Kelleher 48.1 288
289
Kieran Kelleher 49.1 290
291
Kieran Kelleher 50.1 292
293
Kieran Kelleher 51.1 294
295
Kieran Kelleher 52.1 296
297
Kieran Kelleher 53.1 298
299
Kieran Kelleher 54.1 300
301
Kieran Kelleher 45.1 302 This is a useful diagram with overview of core classes and how they are related.