Version 16.1 by Pascal Robert on 2012/01/21 22:06

Hide last authors
Pascal Robert 15.1 1 {{warning}}
2 This is deprecated information!
3 {{/warning}}
4
Pascal Robert 11.1 5 == Overview ==
Pascal Robert 5.1 6
Pascal Robert 13.1 7 {{warning}}
Pascal Robert 16.1 8 //Note: these techniques depend on deprecated technologies. Use at your own risk. - Mike Schrag//
9 //Hey, they don't even exist on Mac OS X 10.5.  It's dead, Jim. - Pascal Robert//
Pascal Robert 13.1 10 {{/warning}}
11
Pascal Robert 5.1 12 A CocoaEOApplication (formally, in WO 5.2, Cocoa Enterprise Objects Application, but here simply CEO) is a set of objects which allow you to use the Enterprise Objects Framework (EOF) inside Cocoa. Even though the technology is not yet supported by Apple, it had been around since NeXT was independent.
13
Pascal Robert 16.1 14 In its previous versions, CEO uses Objectve-C as its primary language (which is now the primary language of Cocoa), but since WO 5.0 the main language to use EOF is Java. In order to use EOF within Cocoa (in particular, in order to use EOModeler in OSX), Apple developed the so-called Java-Bridge (see e.g., [[http:~~/~~/cocoadevcentral.com/articles/000024.php>>url:http://cocoadevcentral.com/articles/000024.php||shape="rect"]] ~-~--there was a document entitled "Using the Java Bridge", but at the time I am writing this (Jul 24 2003), it was not available any more in developer.apple.com; however you may find a copy in your hard disk under /Developer/Documentation/JavaBridge/JavaBridge.pdf). In order to develope a CEO, you will have to get used to its mechanisms.
Pascal Robert 5.1 15
Pascal Robert 16.1 16 //Update (Jun 3, 2005): the JavaBridge.pdf is now in// //[[http:~~/~~/developer.apple.com/documentation/Cocoa/Conceptual/Legacy/JavaBridge/JavaBridge.pdf_>>url:http://developer.apple.com/documentation/Cocoa/Conceptual/Legacy/JavaBridge/JavaBridge.pdf_||shape="rect"]]//
Pascal Robert 5.1 17
Pascal Robert 11.1 18 == Getting Started ==
Pascal Robert 5.1 19
Pascal Robert 16.1 20 There are some things you have to be aware when creating a CEO project (this note is for WO 5.2 and has been revisted for 5.2.2 ~-~--and now for WO 5.2.4):
Pascal Robert 5.1 21
22 When you launch the Project Builder's wizard and ask for a Cocoa/EO application, it will create an unusable MainMenu.nib file. To solve the problem: close your project; delete the "corrupted" file MainMenu.nib (it is inside the English.lproj folder of your project); launch Interface Builder and create an Empty interface; save it as MainMenu.nib (better with the extension) in the same place where the "corrupted" file was; re-launch Project Builder; Done.
23
24 * .... in WO 5.2.2 for OSX 10.3 (Panther+Xcode), the previous bug was fixed.
25 * .... in WO 5.2.4 for OSX 10.4 (Tiger+Xcode), it still works.
26
27 The first time you drag an entity from EOModeler into Interface Builder, an EOEditingContext will be created for you (if you drag the entity onto a window, an NSTableView and an EODisplayGroup will also be created; if you do onto the class-viewer, only the EODisplayGroup will be created). The default (not always desirable) is to "fetch on load" all the objects in the entity; if you do not want this, go to the inspector (with the EODisplayGroup selected) and uncheck the corresponding button.
28
Pascal Robert 16.1 29 Basically, you are there. You can use all actions of your display groups and your editing context to see and manipulate your database, without writing a single line of code ~-~--in fact, you do not have neither to compile your project; simply connect your buttons and run the "Test Interface" command from the file menu (or cmd+r from the keyboard) and you will see your database showing up.
Pascal Robert 5.1 30
31 Of course, if you are an experienced programmer and want to add some "logic" to your app, you can do it. The first thing you have to do, is to choose a language; I prefer Obj-C, but also Java can be used... in fact it will be used even if you decide not to. The EOF had been written in pure Java so, even if you do not write a single line of code in Java (which some times seems unavoidable), you will be using it via the Java Bridge. I'll be back on this later...
32
Pascal Robert 11.1 33 == A "tutorial" ==
Pascal Robert 5.1 34
35 Following the recomendation of a virtual-friend (Arturo Perez), I decided to add a simple guide to build up a CEO.
36
37 It is really simple... almost as simple as D2W, but more beauty.
38
39 Have a model? if so, you can manipulate your db with out writing a single line of code.
Pascal Robert 16.1 40 Try the following:
Pascal Robert 5.1 41
42 * create a Cocoa/EO project importing your model (if you are still in 5.2.1, taking care of recreate the nib file)
43 * open your MainMenu.nib (from PB or Xcode) and drag a new window from the IB's palette
44 * open your model and drag an entity to the window
45 * in IB, press cmd+r
46
Pascal Robert 16.1 47 Your table will show up!!
Pascal Robert 5.1 48
Pascal Robert 11.1 49 If you are familiar with IB, the reset is history, but in case you are not, you can try:
Pascal Robert 5.1 50
Pascal Robert 11.1 51 * drag a button from the palette to you window
52 * connect it (with cntrl+drag) to your display group (it was created in step 3 below) and select the outlet "insert" by double-clicking it
53 * press cmd+r
54 * push the button
Pascal Robert 5.1 55
Pascal Robert 16.1 56 You will see a new row in your table!!
Pascal Robert 5.1 57
Pascal Robert 11.1 58 Do you want to add some custom logic?
Pascal Robert 5.1 59
Pascal Robert 11.1 60 * select the Classes tab, select your root class (I prefere NSObject) and press enter
Pascal Robert 16.1 61 * press cmd+1, add one outlet (displayGroup) and one action (doit
Pascal Robert 11.1 62 * press cmd+opt+f (answer ok to the panel) and press cmd+opt+i
63 * connect your outlets, add a button and connect it to your action. Save.
64 * in PB (or Xcode) you will see your files. Edit them to implement doit:. For example, try something like
Pascal Robert 5.1 65
Pascal Robert 13.1 66 {{code}}
Pascal Robert 5.1 67
Pascal Robert 13.1 68 - (IBAction)doit:(id)sender{
69 [[displayGroup displayedObjects] takeValueForKey:aValue :@"aKey"];
70 }
Pascal Robert 5.1 71
Pascal Robert 13.1 72 {{/code}}
73
Pascal Robert 11.1 74 * in PB (or Xcode) press cmd+r
Pascal Robert 5.1 75
Pascal Robert 11.1 76 You will compile and run your new app. After the table shows up, push the (second) button.
Pascal Robert 5.1 77
Pascal Robert 16.1 78 That's it!! You are now ready to develope the best apps in the market ;^()
Pascal Robert 5.1 79
Pascal Robert 11.1 80 == On Memory Management ==
Pascal Robert 5.1 81
Pascal Robert 11.1 82 If you know you are going to require tones of memory, or you get the "java.lang.outOfMemoryError"; message in your console, or your app is simply not doing what it is supposed to, you may need to read this section... otherwise, simply jump it.
Pascal Robert 5.1 83
Pascal Robert 11.1 84 Are you still here? well, your problem may be that the VJM did not allocate enough memory for you.
Pascal Robert 5.1 85
Pascal Robert 11.1 86 In Project Builder, under targets and with the main target selected, go to Info.plist Entries > Pure-Java Specific (in Xcode, you have to click the little triangle of the targets group, double-click the main target). Add in the "Additional VM Options" text field something like
Pascal Robert 5.1 87
88 {{panel}}
Pascal Robert 16.1 89 -Xms256m -Xmx256m\\
Pascal Robert 5.1 90 {{/panel}}
91
Pascal Robert 11.1 92 (This will give to your JVM 256Mb of memory instead of 64Mb, the default.)
Pascal Robert 5.1 93
Pascal Robert 16.1 94 Also, it is convenient that you get used to use NSAutoreleasePool properly (see its documentation and related topics; in particular, read Chapter 4 of the book "The Objective-C Programming Language" [[http:~~/~~/developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC>>url:http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC||shape="rect"]]). The Java Bridge does a good work using "garbage Collection" in the Java side, and "reference count" in the Obj-C side; just do not worry about that.
Pascal Robert 5.1 95
Pascal Robert 13.1 96 == Morphing Objects ==
Pascal Robert 5.1 97
Pascal Robert 13.1 98 As I said, even if your code uses only Obj-C, you will be dealing with Java objects via the Bridge; therefore you most know how to manipulate those objects "allocated" in the Java side.
Pascal Robert 5.1 99
Pascal Robert 13.1 100 The easiest way to allocate an object in the Java side, from the Obj-C one, is, e.g.,
Pascal Robert 5.1 101
Pascal Robert 13.1 102 {{code}}
Pascal Robert 5.1 103
Pascal Robert 13.1 104 id aJavaObject = [NSClassFromString(@"java.lang.Object") new];
Pascal Robert 5.1 105
Pascal Robert 13.1 106 {{/code}}
Pascal Robert 5.1 107
Pascal Robert 16.1 108 Do not forget to release such an object; since you allocated it, you own it!
Pascal Robert 5.1 109
Pascal Robert 16.1 110 For those objects comming from com.apple.cocoa.foundation.* you really does not have to take care (with the remarkable exception of NSEnumerator); they go forth and back nicely. Even do some simple objects like String (which maps to NSString), Number (which maps to NSNumber) and some basic types like int, char, boolean and so.
Pascal Robert 5.1 111
112 So far, so good. However, be carefull when you want to enumarate an array. In the one hand, if the array was created in the Obj-C side and you ask for an objectEnumerator, you will recive an NSEnumerator. This object implements the method nextObject to traverse the elements of the array. On the other hand, if the array was created in the Java side (e.g., as a result of an allObjects() call) and you ask for an objectEnumerator, you will recive an Enumerator object which implements the nextElement() method instead.
113
114 Some examples of the two scenarios are the following.
115
116 An Obj-C array:
117
Pascal Robert 13.1 118 {{code}}
Pascal Robert 5.1 119
Pascal Robert 15.1 120 NSArray* anObjCArray = [NSArray arrayWithObjects:objA, objB, objC, objD, nil];
Pascal Robert 13.1 121 NSEnumerator* en = [anObjCArray objectEnumerator];
122 id o = nil;
Pascal Robert 5.1 123
Pascal Robert 13.1 124 while(o = [en nextObject]){
125 // here your code using o,
126 // which will traverse all objects in the array
127 }
Pascal Robert 11.1 128
Pascal Robert 13.1 129 {{/code}}
Pascal Robert 5.1 130
131 A Java array:
132
Pascal Robert 13.1 133 {{code}}
Pascal Robert 5.1 134
Pascal Robert 13.1 135 NSArray* aJavaArray = [someDisplayGroup displayedObjects];
136 NSEnumerator* en = [aJavaArray objectEnumerator];
Pascal Robert 5.1 137
Pascal Robert 13.1 138 while([en hasMoreObjects]){
139 id o = [en nextElement];
140 // here your code using o, which will traverse all objects in the array
141 }
Pascal Robert 11.1 142
Pascal Robert 13.1 143 {{/code}}
Pascal Robert 5.1 144
145 These simple examples show some things to have in mind when developping a CEO:
146
147 * Obj-C's methods may accept arbitrarly large lists of objects as parameters (they usually end with nil); Java's do not. If you know your method will be processed by a Java's object, NEVER use such a construction... it will simply do not be recognized. Instead, Java uses arrays of the form
148
Pascal Robert 13.1 149 {{code}}
Pascal Robert 5.1 150
Pascal Robert 13.1 151 new Object[] {objA,objB,objC,objD}
Pascal Robert 5.1 152
Pascal Robert 13.1 153 {{/code}}
Pascal Robert 5.1 154
155 Therefore, you most create FIRST the array and, once allocated, use it as argument. An important example is the class method qualifierWithQualifierFromat(String format, NSArray arguments) implemented by com.webobjects.eocontrol.EOQualifier. I'll be back with this isue later...
156
157 * An NSEnumerator ends its "path" with a nil object; Java's Enumerator throws an exception if it is asked for one more element. Therefore, to avoid such an exception, you have to use the hasMoreElements method.
158 * Objects in the Java side are called from the Obj-C side as if they were Obj-C objects (with the same sintaxis); that is, if the Java object anObject implements the method someMethod(Object someParameter, Object someOther), you have to call it with a line of the form
159
Pascal Robert 13.1 160 {{code}}
Pascal Robert 5.1 161
Pascal Robert 13.1 162 [anObject someMethod:someParameter :someOther];
Pascal Robert 5.1 163
Pascal Robert 13.1 164 {{/code}}
Pascal Robert 5.1 165
Pascal Robert 11.1 166 == Arrays, NSArrays, and more arrays... ==
Pascal Robert 5.1 167
168 Maybe the worst part of the CEO integration is the managment of arrays; there are 5 types of array you most be aware:
169
170 * NSArray; as defined in Foundation.h (Obj-C side).
171 * NSArray; as defined in com.apple.cocoa.foundation (Java side).
172 * NSArray; as defined in com.webobjects.foundation (Java side).
Pascal Robert 16.1 173 * A C array of objects (like id array[]).
174 * A Java array of objects (like Object[]).
Pascal Robert 5.1 175
Pascal Robert 16.1 176 This is too much!!!! But life is life, and beter get used to them...
Pascal Robert 5.1 177
Pascal Robert 16.1 178 Usually, when you are not intended to use EOF inside Cocoa, you can forget about the array in com.webobjects.foundation; the bridge translates, by default, that of Foundation.h to com.apple.cocoa.foundation, back and forth, with no problem... well, almost: from Java to Obj-C each array go as an NSCFArray which is documented NOWARE, but it works as an NSArray. On the other side, from Obj-C to Java, they go as com.apple.cocoa.NSMutableArray which inherits from the usual NSArray ~-~--try to print their classes description in both sides after they had traveled the bridge with the following code:
Pascal Robert 5.1 179
Pascal Robert 13.1 180 {{code}}
Pascal Robert 5.1 181
Pascal Robert 13.1 182 [textField setStringValue:[[aJavaArray class] description]];
Pascal Robert 5.1 183
Pascal Robert 13.1 184 {{/code}}
Pascal Robert 5.1 185
186 or
187
Pascal Robert 13.1 188 {{code}}
Pascal Robert 5.1 189
Pascal Robert 11.1 190 textField.setStringValue(anObjCArray.getClass().toString());
Pascal Robert 5.1 191
Pascal Robert 13.1 192 {{/code}}
Pascal Robert 5.1 193
194 This is not as bad as it may seems. In practice, you do not have to be aware of this.
195
Pascal Robert 16.1 196 The real problem start when you want to use EOF. Most of the Enterprise Objects (defined somewhere in com.webobjects.*), when use an array, they are waiting for a com.webobjects.foundation.NSArray and non of the above.
Pascal Robert 5.1 197
198 There are many solutions to this problem. The first one (for those who knows Java and Obj-C) is to have a bridgeTool.java object in charge of allocating those arrays as neaded:
199
Pascal Robert 13.1 200 {{code}}
Pascal Robert 5.1 201
Pascal Robert 15.1 202 public com.webobjects.foundation.NSArray arrayFromArray(com.apple.cocoa.foundation.NSArray cocoaArray)
Pascal Robert 13.1 203 {
204 com.webobjects.foundation.NSMutableArray woArray =
205 new com.webobjects.foundation.NSMutableArray();
206 int i;
207 for(i=0;i<cocoaArray.count();++i){
208 woArray.addObject(cocoaArray.objectAtIndex(i));
209 }
210 return woArray;
211 }
Pascal Robert 5.1 212
Pascal Robert 13.1 213 {{/code}}
Pascal Robert 5.1 214
215 And call this method from your Obj-C object with something like
216
Pascal Robert 13.1 217 {{code}}
Pascal Robert 5.1 218
Pascal Robert 13.1 219 NSArray* cocoaArray; // suppose this exists
220 id bridgeTool = [[NSClassFromString(@"bridgeTool") new] autorelease];
221 id woArray = [bridgeTool arrayFromArray:cocoaArray];
Pascal Robert 5.1 222
Pascal Robert 13.1 223 {{/code}}
Pascal Robert 5.1 224
225 Once you have your woArray, you can use it as an argument in Enterprise Objects' calls.
226
Pascal Robert 16.1 227 I had not foud the way to call a Java method which has an Object[] array as argument. If someone reading this finds out, please let us know...
Pascal Robert 5.1 228
Pascal Robert 11.1 229 == Building qualifiers ==
Pascal Robert 5.1 230
231 When working with EOF, there are several points when you will want to build a qualifier to perform a fetch. A qualifier is an instance of com.webobjects.eocontrol.EOQualifier. If your qualifier does not include any date, then it is as easy as
232
Pascal Robert 13.1 233 {{code}}
Pascal Robert 5.1 234
Pascal Robert 13.1 235 NSString* partOfAName; // suppose this exists
236 NSString* qualifierFormat = [NSString stringWithFormat:@"name caseInsensitiveLike '*%@*'", partOfAName];
237 id qualifier = [NSClassFromString(@"com.webobjects.eocontrol.EOQualifier") qualifierWithQualifierFormat:qualifierFormat :nil];
Pascal Robert 5.1 238
Pascal Robert 13.1 239 {{/code}}
Pascal Robert 5.1 240
241 and then use it in a fetch specification with
242
Pascal Robert 13.1 243 {{code}}
Pascal Robert 5.1 244
Pascal Robert 15.1 245 id fetchSpecification =
Pascal Robert 13.1 246 [[NSClassFromString(@"com.webobjects.eocontrol.EOFetchSpecification")
247 new] autorelease];
248 [fetchSpecification setEntity:@"myEntity"];
249 [fetchSpecification setQualifier:qualifier];
Pascal Robert 5.1 250
Pascal Robert 13.1 251 {{/code}}
Pascal Robert 5.1 252
Pascal Robert 11.1 253 On the other hand, if you want to use a date in your qualifier, since NSCalendarDate is not properly translated to NSTimestamp, you have to use again the bridgeTool.java trick:
Pascal Robert 5.1 254
Pascal Robert 13.1 255 {{code}}
Pascal Robert 5.1 256
Pascal Robert 15.1 257 public NSTimestamp nsTimestampFromString(String dateString){
Pascal Robert 13.1 258 NSTimestampFormatter formatter = new NSTimestampFormatter("%d %m %Y");
259 ParsePosition pp = new ParsePosition(0);
260 NSTimestamp myNSTimestamp = (NSTimestamp)formatter.parseObject(dateString, pp);
261 return myNSTimestamp;
262 }
Pascal Robert 5.1 263
Pascal Robert 13.1 264 {{/code}}
Pascal Robert 5.1 265
266 and call this from Obj-C by
267
Pascal Robert 13.1 268 {{code}}
Pascal Robert 5.1 269
Pascal Robert 15.1 270 NSCalendarDate aCalendarDate; // suppose this exists
Pascal Robert 13.1 271 [aCalendarDate setCalendarFormat:@"%d %m %Y"];
272 id nsTimestamp = [bridgeTool nsTimestampFromString:[aCalendarDate description]];
Pascal Robert 5.1 273
Pascal Robert 13.1 274 {{/code}}
Pascal Robert 5.1 275
276 (just be consistent with your formats).
277
278 It is also possible (at least in WO 5.2.4 running on Tiger) to use
Pascal Robert 16.1 279 com.webobjects.eointerface.cocoa.EOCocoaUtilities as follows:
Pascal Robert 5.1 280
Pascal Robert 13.1 281 {{code}}
Pascal Robert 5.1 282
Pascal Robert 15.1 283 id objPath = @"com.webobjects.eointerface.cocoa.EOCocoaUtilities";
Pascal Robert 13.1 284 id aDate = [NSCalendarDate date];
285 id nsTimestamp = [NSClassFromString(objPath) timestampForGregorianDate:aDate];
Pascal Robert 5.1 286
Pascal Robert 13.1 287 {{/code}}
Pascal Robert 5.1 288
Pascal Robert 16.1 289 From here, you can use the nsTimestamp as argument in those classes defined in com.webobjects.* (e.g., in your qualifiers).
Pascal Robert 5.1 290
Pascal Robert 11.1 291 == Putting things together ==
Pascal Robert 5.1 292
Pascal Robert 16.1 293 Let us start this section describing how to put together two of the most powerful frameworks of OS X; namely, EOF and the NSDocument framework (this section was written, in July 2005, while working in WO 5.2.4 running on Tiger) ~-~--later on, we will integrate these with Core Data which I think that, jointly with Bindings, will substitute EOF in the near future.
Pascal Robert 5.1 294
295 Let us do it as a "tutorial":
296
Pascal Robert 16.1 297 * Start Xcode (I am using 2.0) and select File>New Project. In the next panel choose "Cocoa-Java Document-based Application". This generates a project which is ready to use the Java Bridge and implements the NSDocument architecture ~-~--take a look to the generated files and to the target; in particular, double-click on the main target and inspect all details showed.
Pascal Robert 5.1 298 * In the project window, select the Frameworks folder. From the Project menu, select Add to Project (cmd+alt+a). In the browser, go to System>Library>Frameworks and select JavaEOCocoa.framework; click OK.
299 * Again, press cmd+alt+a, browse to System>Library>Frameworks then to JavaEOAccess.framework>;Resources>Java and select javaeoaccess.jar; click OK.
300 * Repeat the previous step for: javaeocontrol.jar, javaeointerface.jar, javaeointerfacecocoa.jar, javafoundation.jar and javaxml.jar.
Pascal Robert 16.1 301 * Add your model ~-~--double-check that the folder of it is shown in blue colour (instead of yellow).
Pascal Robert 5.1 302
Pascal Robert 16.1 303 Basically you are done. However, if you ~-~-(% style="text-decoration: line-through;" %)like me(%%)~-~- prefer to work in Objective-C, then do the following:
Pascal Robert 5.1 304
305 * Start Interface Builder by double-clicking the MyDocument.nib file.
306 * Click the File's Owner and then the Classes tab. You will notice that the selected class is MyDocument which inherits from NSDocument. Select NSDocument and press "enter" to create a new subclass; give it a name (say, MyCDocument). Then from the Classes menu, select "Create Files..." and accept.
307 * Return to the Instances tab, select the File's Owner and in the "Custom class" inspector (cmd+5) select the new created class. Save and hide.
308 * Double-click the main target of your project. Select "Document types" and edit accordingly to the new class (e.g., changing MyDocument for MyCDocument).
309 * In the .m file created three steps below, add the following:
310
Pascal Robert 13.1 311 {{code}}
Pascal Robert 5.1 312
Pascal Robert 13.1 313 - (NSString *)windowNibName
314 {
315 return @"MyDocument";
316 }
Pascal Robert 5.1 317
Pascal Robert 13.1 318 {{/code}}
Pascal Robert 5.1 319
320 Compile and run (cmd+r).
321
322 Finally, if you will, you can get rid of the warnings about deprecated methods in the .java file created by the wizard:
323
324 * From the project window, delete the MyDocument.java file.
Pascal Robert 16.1 325 * Check the button "Needs Java" in the "Cocoa Java Specific" pane of your main target ~-~--in the window opened three steps below.
Pascal Robert 5.1 326
327 Compile and run (cmd+r).
328
329 By this moment, you had created a multi-document CEO...
330
Pascal Robert 16.1 331 - StrauszRicardo