Version 9.1 by smmccraw on 2007/07/08 09:45

Show last authors
1 == Overview ==
2
3 //Note: these techniques depend on deprecated technologies. Use at your own risk. ~-~- Mike Schrag//
4
5 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.
6
7 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 ~-~--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.
8
9 //Update (Jun 3, 2005): the JavaBridge.pdf is now in http:~/~/developer.apple.com/documentation/Cocoa/Conceptual/Legacy/JavaBridge/JavaBridge.pdf_//
10
11 == Getting Started ==
12
13 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):
14
15 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.
16
17 * .... in WO 5.2.2 for OSX 10.3 (Panther+Xcode), the previous bug was fixed.
18 * .... in WO 5.2.4 for OSX 10.4 (Tiger+Xcode), it still works.
19
20 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.
21
22 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.
23
24 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...
25
26 == A "tutorial" ==
27
28 Following the recomendation of a virtual-friend (Arturo Perez), I decided to add a simple guide to build up a CEO.
29
30 It is really simple... almost as simple as D2W, but more beauty.
31
32 Have a model? if so, you can manipulate your db with out writing a single line of code.
33 Try the following:
34
35 * create a Cocoa/EO project importing your model (if you are still in 5.2.1, taking care of recreate the nib file)
36 * open your MainMenu.nib (from PB or Xcode) and drag a new window from the IB's palette
37 * open your model and drag an entity to the window
38 * in IB, press cmd+r
39
40 Your table will show up!!
41
42 If//you//are//familiar//with//IB,//the//reset//is//history,//but//in//case//you//are//not,//you//can//try:
43
44 * //dragg//a//button//from//the//palette//to//you//window//
45 * //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//
46 * //press//cmd+r
47 * //push//the//button//
48
49 You//will//see//a//new//row//in//your//table!!
50
51 Do//you//want//to//add//some//custom//logic?//
52
53 9.//select//the//Classes//tab,//select//your//root//class//(I//prefere//NSObject)//and//press//enter
54 10.//press//cmd+1,//add//one//outlet//(displayGroup)//and//one//action//(doit:)
55 11.//press//cmd+opt+f//(answer//ok//to//the//panel)//and//press//cmd+opt+i
56 12.//connect//your//outlets,//add//a//button//and//connect//it//to//your//action.//Save.
57 13.//in//PB//(or//Xcode)//you//will//see//your//files.//Edit//them//to//implement//doit:._For_example,//try//something//like
58
59 {{panel}}
60
61 __-_(IBAction)doit:(id)sender{
62 ______[(displayGroup_displayedObjects]_takeValueForKey:aValue_:@"aKey"];
63 __}
64
65 {{/panel}}
66
67 * //in//PB//(or//Xcode)//press//cmd+r
68
69 You//will//compile//and//run//your//new//app.//After//the//table//shows//up,//push//the//(second)//button.
70
71 That's//it!!
72 You//are//now//ready//to//develope//the//best//apps//in//the//market//;^^()^^//
73
74 == On//Memory//Management ==
75
76 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.
77
78 Are//you//still//here?//well,//your//problem//may//be//that//the//VJM//did//not//allocate//enough//memory//for//you.
79
80 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
81
82 {{panel}}
83
84 __-Xms256m_-Xmx256m
85
86 {{/panel}}
87
88 (This//will//give//to//your//JVM//256Mb//of//memory//instead//of//64Mb,//the//default.)//
89
90 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_)._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.
91
92 == Morphing//Objects// ==
93
94 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.//
95
96 The//easiest//way//to//allocate//an//object//in//the//Java//side,//from//the//Obj-C//one,//is,//e.g.,
97
98 {{panel}}
99
100 __id_aJavaObject_=_[NSClassFromString(at"java.lang.Object")_new];
101
102 {{/panel}}
103
104 Do//not//forget//to//release//such//an//object;//since//you//allocated//it,//you//own//it!
105
106 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.**
107
108 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.
109
110 Some examples of the two scenarios are the following.
111
112 An Obj-C array:
113
114 {{panel}}
115
116 NSArray* anObjCArray = [NSArray arrayWithObjects.objA, objB, objC, objD, nil];
117 NSEnumerator* en = [anObjCArray objectEnumerator];
118 id o = nil;
119
120 while(o = [en nextObject]){
121 // here your code using o,
122 // which will traverse all objects in the array
123 }
124
125 {{/panel}}
126
127 A Java array:
128
129 {{panel}}
130
131 NSArray* aJavaArray = [someDisplayGroup displayedObjects];
132 NSEnumerator* en = [aJavaArray objectEnumerator];
133
134 while([en hasMoreObjects]){
135 id o = [en nextElement];
136 // here your code using o, which will traverse all objects in the array
137 }
138
139 {{/panel}}
140
141 These simple examples show some things to have in mind when developping a CEO:
142
143 * 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
144
145 {{panel}}
146
147 new Object[] {objA,objB,objC,objD}
148
149 {{/panel}}
150
151 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...
152
153 * 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.
154 * 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
155
156 {{panel}}
157
158 [anObject someMethod.someParameter .someOther];
159
160 {{/panel}}
161
162 == Arrays, NSArrays, and more arrays... ==
163
164 Maybe the worst part of the CEO integration is the managment of arrays; there are 5 types of array you most be aware:
165
166 * NSArray; as defined in Foundation.h (Obj-C side).
167 * NSArray; as defined in com.apple.cocoa.foundation (Java side).
168 * NSArray; as defined in com.webobjects.foundation (Java side).
169 * A C array of objects (like id array[[]]).
170 * A Java array of objects (like Object[[]]).
171
172 This is too much!!!! But life is life, and beter get used to them...
173
174 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:
175
176 {{panel}}
177
178 [textField setStringValue.((aJavaArray class] description]];
179
180 {{/panel}}
181
182 or
183
184 {{panel}}
185
186 textField.setStringValue(anObjCArray.getClass().toString());
187
188 {{/panel}}
189
190 This is not as bad as it may seems. In practice, you do not have to be aware of this.
191
192 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.**
193
194 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:
195
196 {{panel}}
197
198 public com.webobjects.foundation.NSArray arrayFromArray(com.apple.cocoa.foundation.NSArray cocoaArray)
199 {
200 com.webobjects.foundation.NSMutableArray woArray =
201 new com.webobjects.foundation.NSMutableArray();
202 int i;
203 for(i=0;i<cocoaArray.count();++i){
204 woArray.addObject(cocoaArray.objectAtIndex(i));
205 }
206 return woArray;
207 }
208
209 {{/panel}}
210
211 And call this method from your Obj-C object with something like
212
213 {{panel}}
214
215 NSArray* cocoaArray; // suppose this exists
216 id bridgeTool = [(NSClassFromString(at"bridgeTool") new] autorelease];
217 id woArray = [bridgeTool arrayFromArray.cocoaArray];
218
219 {{/panel}}
220
221 Once you have your woArray, you can use it as an argument in Enterprise Objects' calls.
222
223 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...
224
225 == Building qualifiers ==
226
227 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
228
229 {{panel}}
230
231 NSString* partOfAName; // suppose this exists
232 NSString* qualifierFormat = [NSString stringWithFormat%3aat"name caseInsensitiveLike '*%at*'", partOfAName];
233 id qualifier = [NSClassFromString(at"com.webobjects.eocontrol.EOQualifier") qualifierWithQualifierFormat.qualifierFormat .nil];
234
235 {{/panel}}
236
237 and then use it in a fetch specification with
238
239 {{panel}}
240
241 id fetchSpecification =
242 [[NSClassFromString(@"com.webobjects.eocontrol.EOFetchSpecification")
243 new] autorelease];
244 [fetchSpecification setEntity.at"myEntity"];
245 [fetchSpecification setQualifier.qualifier];
246
247 {{/panel}}
248
249 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:
250
251 {{panel}}
252
253 public NSTimestamp nsTimestampFromString(String dateString){
254 NSTimestampFormatter formatter = new NSTimestampFormatter("%d %m %Y");
255 ParsePosition pp = new ParsePosition(0);
256 NSTimestamp myNSTimestamp = (NSTimestamp)formatter.parseObject(dateString, pp);
257 return myNSTimestamp;
258 }
259
260 {{/panel}}
261
262 and call this from Obj-C by
263
264 {{panel}}
265
266 NSCalendarDate aCalendarDate; // suppose this exists
267 [aCalendarDate setCalendarFormat%3aat"%d %m %Y"];
268 id nsTimestamp = [bridgeTool nsTimestampFromString.(aCalendarDate description]];
269
270 {{/panel}}
271
272 (just be consistent with your formats).
273
274 It is also possible (at least in WO 5.2.4 running on Tiger) to use
275 com.webobjects.eointerface.cocoa.EOCocoaUtilities as follows:
276
277 {{panel}}
278
279 id objPath = @"com.webobjects.eointerface.cocoa.EOCocoaUtilities";
280 id aDate = [NSCalendarDate date];
281 id nsTimestamp = [NSClassFromString(objPath) timestampForGregorianDate.aDate];
282
283 {{/panel}}
284
285 From here, you can use the nsTimestamp as argument in those classes defined in com.webobjects.** (e.g., in your qualifiers).**
286
287 == Putting things together ==
288
289 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.
290
291 Let us do it as a "tutorial":
292
293 * 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.
294 * 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.
295 * Again, press cmd+alt+a, browse to System>Library>Frameworks then to JavaEOAccess.framework>;Resources>Java and select javaeoaccess.jar; click OK.
296 * Repeat the previous step for: javaeocontrol.jar, javaeointerface.jar, javaeointerfacecocoa.jar, javafoundation.jar and javaxml.jar.
297 * Add your model ~-~--double-check that the folder of it is shown in blue colour (instead of yellow).
298
299 Basically you are done. However, if you ~-~--like me~-~-- prefer to work in Objective-C, then do the following:
300
301 * Start Interface Builder by double-clicking the MyDocument.nib file.
302 * 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.
303 * 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.
304 * Double-click the main target of your project. Select "Document types" and edit accordingly to the new class (e.g., changing MyDocument for MyCDocument).
305 * In the .m file created three steps below, add the following:
306
307 {{panel}}
308
309 - (NSString *)windowNibName
310 {
311 return @"MyDocument";
312 }
313
314 {{/panel}}
315
316 Compile and run (cmd+r).
317
318 Finally, if you will, you can get rid of the warnings about deprecated methods in the .java file created by the wizard:
319
320 * From the project window, delete the MyDocument.java file.
321 * Check the button "Needs Java" in the "Cocoa Java Specific" pane of your main target ~-~--in the window opened three steps below.
322
323 Compile and run (cmd+r).
324
325 By this moment, you had created a multi-document CEO...
326
327 *
328 ** StrauszRicardo
329
330 Category:WebObjects