Wiki source code of EOF-Using EOF-Custom EOAdaptor

Last modified by Pascal Robert on 2012/09/22 15:33

Hide last authors
Pascal Robert 8.1 1 It's surprisingly easy to create a simple custom EOAdaptor. You can look at JavaFSAdaptor, which is part of Project Wonder, and you can also look at JavaMemoryAdaptor, also in Wonder.
smmccraw 1.1 2
Pascal Robert 6.1 3 Here is a little example on how the create an EOAdaptor:
4
Pascal Robert 7.1 5 Creating an EOAdaptor can be very straightforward (or very complicated)
Pascal Robert 8.1 6 depending on the underlying data source. In this example, and for the
7 sake of keeping thing simple, I'm using the file system as a data source
8 (well, at least, how java.io.File sees it).
9 At a bare minimum you need to implement three classes: EOAdaptor,
10 EOAdaptorContext, EOAdaptorChannel.
Pascal Robert 6.1 11
Pascal Robert 8.1 12 ~[[[EOAdaptor JavaDoc>>url:http://wocommunity.org/documents/javadoc/WebObjects/5.4.2/com/webobjects/eoaccess/EOAdaptor.html?is-external=true||shape="rect"]]]
Pascal Robert 6.1 13
Pascal Robert 7.1 14 Not much in here. The EOAdaptor is mostly a rendezvous point to access
Pascal Robert 8.1 15 other functionality in your adaptor. You need to implement a couple of
16 abstract methods though:
Pascal Robert 6.1 17
Pascal Robert 8.1 18 (% class="alternate" %)
Pascal Robert 7.1 19 * assertConnectionDictionaryIsValid() to check the validity of a
Pascal Robert 8.1 20 connection dictionary.
Pascal Robert 6.1 21 * createAdaptorContext() to create your own EOAdaptorContext instance.
22 * defaultExpressionClass() to return a relevant EOSQLExpression class.
Pascal Robert 7.1 23 * isValidQualifierType() to decide if a qualifier can be used in a SQL
Pascal Robert 8.1 24 where clause.
Pascal Robert 6.1 25 * synchronizationFactory() to return a EOSchemaGeneration helper.
26
27 In the case of a file system there is not much to do:
28
29 {{code}}
30
Pascal Robert 7.1 31 public void assertConnectionDictionaryIsValid()
Pascal Robert 6.1 32 {
33 }
Pascal Robert 7.1 34
Pascal Robert 6.1 35 public EOAdaptorContext createAdaptorContext()
36 {
37 return new FSAdaptorContext( this );
38 }
Pascal Robert 7.1 39
Pascal Robert 6.1 40 public Class defaultExpressionClass()
41 {
42 throw new UnsupportedOperationException
43 ( "FSAdaptor.defaultExpressionClass" );
44 }
Pascal Robert 7.1 45
Pascal Robert 6.1 46 public EOSQLExpressionFactory expressionFactory()
47 {
48 throw new UnsupportedOperationException
49 ( "FSAdaptor.expressionFactory" );
50 }
Pascal Robert 7.1 51
Pascal Robert 6.1 52 public boolean isValidQualifierType(String aTypeName, EOModel aModel)
53 {
54 return true;
55 }
Pascal Robert 7.1 56
Pascal Robert 6.1 57 public EOSchemaGeneration synchronizationFactory()
58 {
59 throw new UnsupportedOperationException
60 ( "FSAdaptor.synchronizationFactory" );
61 }
62
63 {{/code}}
64
65 The only important method is createAdaptorContext().
66
Pascal Robert 8.1 67 ~[[[EOAdaptorContext JavaDoc>>url:http://wocommunity.org/documents/javadoc/WebObjects/5.4.2/com/webobjects/eoaccess/EOAdaptorContext.html?is-external=true||shape="rect"]]]
Pascal Robert 6.1 68
Pascal Robert 7.1 69 The adaptor context handles "transactions". It's usually pretty simple
Pascal Robert 8.1 70 if your data store support that concept. Not much to do here for a file
71 system:
Pascal Robert 6.1 72
73 {{code}}
74
Pascal Robert 7.1 75 public void beginTransaction()
Pascal Robert 6.1 76 {
77 if ( _hasTransaction == false )
78 {
79 _hasTransaction = true;
80
81 this.transactionDidBegin();
82 }
83 }
Pascal Robert 7.1 84
Pascal Robert 6.1 85 public void commitTransaction()
86 {
87 if ( _hasTransaction == true )
88 {
89 _hasTransaction = false;
Pascal Robert 7.1 90
Pascal Robert 6.1 91 this.transactionDidCommit();
92 }
93 }
Pascal Robert 7.1 94
Pascal Robert 6.1 95 public EOAdaptorChannel createAdaptorChannel()
96 {
97 return new FSAdaptorChannel( this );
98 }
Pascal Robert 7.1 99
Pascal Robert 6.1 100 public void handleDroppedConnection()
101 {
102 }
103
104 public void rollbackTransaction()
105 {
106 throw new UnsupportedOperationException
107 ( "FSAdaptorContext.rollbackTransaction" );
108 }
109
110 {{/code}}
111
Pascal Robert 7.1 112 Again createAdaptorChannel() is the only method that you really care
Pascal Robert 8.1 113 about in this case.
Pascal Robert 6.1 114
Pascal Robert 8.1 115 ~[[[EOAdaptorChannel JavaDoc>>url:http://wocommunity.org/documents/javadoc/WebObjects/5.4.2/com/webobjects/eoaccess/EOAdaptorChannel.html?is-external=true||shape="rect"]]]
Pascal Robert 6.1 116
Pascal Robert 7.1 117 That's the real thing. It's where most of the work is done. It's
Pascal Robert 8.1 118 important to get this one right. Depending on your back end, it's simply
119 an exercise in translating EOQualifiers into something that your data
120 storage will understand. Here a the most important methods:
Pascal Robert 6.1 121
Pascal Robert 8.1 122 (% class="alternate" %)
Pascal Robert 7.1 123 * deleteRowsDescribedByQualifier() to handle deletion of "row" or
Pascal Robert 8.1 124 something equivalent.
Pascal Robert 6.1 125 * insertRow() to create a "row".
126 * updateValuesInRowsDescribedByQualifier() to update a "row".
127
Pascal Robert 7.1 128 Finally, fetching "rows" is a two steps process: first you need to setup
Pascal Robert 8.1 129 the "context" of what you are going to fetch and then fetch it:
Pascal Robert 6.1 130
Pascal Robert 8.1 131 (% class="alternate" %)
Pascal Robert 6.1 132 * selectAttributes() is where you set your fetch "context".
133 * fetchRow is the actual fetching one row at the time.
134
135 In the case of a file system here is what you could have:
136
Pascal Robert 8.1 137 (% class="alternate" %)
Pascal Robert 7.1 138 * deleteRowsDescribedByQualifier will delete some files (aka "rows"
Pascal Robert 8.1 139 based) on a qualifier.
Pascal Robert 6.1 140
141 {{code}}
142
Pascal Robert 7.1 143 public int deleteRowsDescribedByQualifier(EOQualifier aQualifier,
Pascal Robert 6.1 144 EOEntity anEntity)
145 {
146 if ( aQualifier != null )
147 {
148 if ( anEntity != null )
149 {
Pascal Robert 7.1 150 NSArray someFiles =
Pascal Robert 6.1 151 FSQualifierHandler.filesWithQualifier( aQualifier );
Pascal Robert 7.1 152
Pascal Robert 6.1 153 if ( someFiles != null )
154 {
155 someFiles = this.filteredArrayWithEntity( someFiles, anEntity );
Pascal Robert 7.1 156
Pascal Robert 6.1 157 if ( someFiles != null )
158 {
159 int count = someFiles.count();
160 int counter = 0;
Pascal Robert 7.1 161
Pascal Robert 6.1 162 for ( int index = 0; index < count; index++ )
163 {
164 File aFile = (File) someFiles.objectAtIndex( index );
Pascal Robert 7.1 165
Pascal Robert 6.1 166 if ( aFile.delete() == true )
167 {
168 counter += 1;
169 }
170 }
Pascal Robert 7.1 171
Pascal Robert 6.1 172 return counter;
173 }
174 }
175
176 return 0;
177 }
178
179 throw new IllegalArgumentException
180 ( "FSAdaptorChannel.deleteRowsDescribedByQualifier: null entity." );
181 }
182
183 throw new IllegalArgumentException
184 ( "FSAdaptorChannel.deleteRowsDescribedByQualifier: null qualifier." );
185 }
186
187 {{/code}}
188
Pascal Robert 8.1 189 (% class="alternate" %)
190 * insertRow will create a file or directory.
Pascal Robert 6.1 191
192 {{code}}
193
Pascal Robert 7.1 194 public void insertRow(NSDictionary aRow, EOEntity anEntity)
Pascal Robert 6.1 195 {
196 if ( aRow != null )
197 {
198 if ( anEntity != null )
199 {
200 String aPath = (String) aRow.objectForKey( "absolutePath" );
Pascal Robert 7.1 201
Pascal Robert 6.1 202 if ( aPath != null )
203 {
204 File aFile = new File( aPath );
Pascal Robert 7.1 205
Pascal Robert 6.1 206 try
207 {
208 if ( anEntity.name().equals( "FSDirectory" ) == true )
209 {
210 aFile.mkdirs();
211 }
212 else
213 {
214 aFile.createNewFile();
215 }
216 }
217 catch(Exception anException)
218 {
219 throw new RuntimeException
220 ( "FSAdaptorChannel.insertRow: " + anException );
221 }
Pascal Robert 7.1 222
Pascal Robert 6.1 223 return;
224 }
225
226 throw new IllegalArgumentException
227 ( "FSAdaptorChannel.insertRow: null absolutePath." );
228 }
229
230 throw new IllegalArgumentException
231 ( "FSAdaptorChannel.insertRow: null entity." );
232 }
Pascal Robert 7.1 233
Pascal Robert 6.1 234 throw new IllegalArgumentException
235 ( "FSAdaptorChannel.insertRow: null row." );
236 }
237
238 {{/code}}
239
Pascal Robert 8.1 240 (% class="alternate" %)
241 * updateValuesInRowsDescribedByQualifier will change a file attributes.
Pascal Robert 6.1 242
243 {{code}}
244
Pascal Robert 7.1 245 public int updateValuesInRowsDescribedByQualifier(NSDictionary
Pascal Robert 6.1 246 aRow, EOQualifier aQualifier, EOEntity anEntity)
247 {
248 if ( aRow != null )
249 {
250 if ( aQualifier != null )
251 {
252 if ( anEntity != null )
253 {
Pascal Robert 7.1 254 NSArray someFiles =
Pascal Robert 6.1 255 FSQualifierHandler.filesWithQualifier( aQualifier );
Pascal Robert 7.1 256
Pascal Robert 6.1 257 if ( someFiles != null )
258 {
259 someFiles = this.filteredArrayWithEntity( someFiles, anEntity );
Pascal Robert 7.1 260
Pascal Robert 6.1 261 if ( someFiles != null )
262 {
263 int count = someFiles.count();
Pascal Robert 7.1 264
Pascal Robert 6.1 265 for ( int index = 0; index < count; index++ )
266 {
267 File aFile = (File) someFiles.objectAtIndex( index );
268 NSArray someKeys = aRow.allKeys();
269 int keyCount = someKeys.count();
Pascal Robert 7.1 270
Pascal Robert 6.1 271 for ( int keyIndex = 0; keyIndex < count; keyIndex++ )
272 {
273 Object aKey = someKeys.objectAtIndex( keyIndex );
Pascal Robert 7.1 274 EOAttribute anAttribute =
Pascal Robert 6.1 275 anEntity.attributeNamed( aKey.toString() );
Pascal Robert 7.1 276
Pascal Robert 6.1 277 if ( anAttribute != null )
278 {
279 Object aValue = aRow.objectForKey( aKey );
Pascal Robert 7.1 280
281
282 NSKeyValueCoding.DefaultImplementation.takeValueForKey( aFile, aValue,
Pascal Robert 6.1 283 anAttribute.columnName() );
284 }
285 }
Pascal Robert 7.1 286
Pascal Robert 6.1 287 }
Pascal Robert 7.1 288
Pascal Robert 6.1 289 return count;
290 }
291 }
292
293 return 0;
294 }
295
296 throw new IllegalArgumentException
Pascal Robert 7.1 297 ( "FSAdaptorChannel.updateValuesInRowsDescribedByQualifier: null
Pascal Robert 6.1 298 entity." );
299 }
Pascal Robert 7.1 300
Pascal Robert 6.1 301 throw new IllegalArgumentException
Pascal Robert 7.1 302 ( "FSAdaptorChannel.updateValuesInRowsDescribedByQualifier: null
Pascal Robert 6.1 303 qualifier." );
304 }
Pascal Robert 7.1 305
Pascal Robert 6.1 306 throw new IllegalArgumentException
307 ( "FSAdaptorChannel.updateValuesInRowsDescribedByQualifier: null row." );
308 }
309
310 {{/code}}
311
Pascal Robert 8.1 312 (% class="alternate" %)
313 * selectAttributes will retrieve some files based on a qualifier.
Pascal Robert 6.1 314
315 {{code}}
316
Pascal Robert 7.1 317 public void selectAttributes(NSArray someAttributes,
318 EOFetchSpecification aFetchSpecification, boolean shouldLock, EOEntity
Pascal Robert 6.1 319 anEntity)
320 {
321 if ( someAttributes != null )
322 {
323 this.setAttributesToFetch( someAttributes );
Pascal Robert 7.1 324
Pascal Robert 6.1 325 if ( aFetchSpecification != null )
326 {
327 if ( anEntity != null )
328 {
Pascal Robert 7.1 329 NSArray someFiles =
Pascal Robert 6.1 330 FSQualifierHandler.filesWithQualifier( aFetchSpecification.qualifier() );
Pascal Robert 7.1 331
Pascal Robert 6.1 332 if ( someFiles != null )
333 {
334 NSArray someSortOrderings = aFetchSpecification.sortOrderings();
Pascal Robert 7.1 335
Pascal Robert 6.1 336 if ( someSortOrderings != null )
337 {
Pascal Robert 7.1 338 someFiles =
339 EOSortOrdering.sortedArrayUsingKeyOrderArray( someFiles,
Pascal Robert 6.1 340 someSortOrderings );
341 }
Pascal Robert 7.1 342
Pascal Robert 6.1 343 someFiles = this.filteredArrayWithEntity( someFiles, anEntity );
Pascal Robert 7.1 344
Pascal Robert 6.1 345 if ( someFiles != null )
346 {
347 this.files().addObjectsFromArray( someFiles );
348 }
349 }
Pascal Robert 7.1 350
Pascal Robert 6.1 351 return;
352 }
353
354 throw new IllegalArgumentException
355 ( "FSAdaptorChannel.selectAttributes: null entity." );
356 }
357
358 throw new IllegalArgumentException
359 ( "FSAdaptorChannel.selectAttributes: null fetch specification." );
360 }
Pascal Robert 7.1 361
Pascal Robert 6.1 362 throw new IllegalArgumentException
363 ( "FSAdaptorChannel.selectAttributes: null attributes." );
364 }
365
366 {{/code}}
367
Pascal Robert 8.1 368 (% class="alternate" %)
369 * fetchRow will "fetch" a dictionary representation of a file.
Pascal Robert 6.1 370
371 {{code}}
372
Pascal Robert 7.1 373 public NSMutableDictionary fetchRow()
Pascal Robert 6.1 374 {
375 File aFile = (File) this.files().lastObject();
Pascal Robert 7.1 376
Pascal Robert 6.1 377 if ( aFile != null )
378 {
379 this.files().removeLastObject();
Pascal Robert 7.1 380
381 return this.dictionaryForFileWithAttributes( aFile,
Pascal Robert 6.1 382 this.attributesToFetch() );
383 }
Pascal Robert 7.1 384
Pascal Robert 6.1 385 return null;
386 }
387
388 {{/code}}
389
Pascal Robert 7.1 390 That could be it. Nothing to involved. However there is two extra
Pascal Robert 8.1 391 methods that make sense to implement: describeTableNames and
392 describeModelWithTableNames.
Pascal Robert 6.1 393
Pascal Robert 8.1 394 (% class="alternate" %)
Pascal Robert 7.1 395 * describeTableNames() return an array of "table" (or something
Pascal Robert 8.1 396 conceptually equivalent) existing in you data back end.
Pascal Robert 7.1 397 * describeModelWithTableNames() will create a default model for some of
Pascal Robert 8.1 398 these "tables".
Pascal Robert 6.1 399
Pascal Robert 7.1 400 This is very useful to get you started in EOModeler instead of having to
Pascal Robert 8.1 401 create the entire model by hand. In the case of a file system, there is
402 only one model:
Pascal Robert 6.1 403
404 {{code}}
405
Pascal Robert 7.1 406 public EOModel describeModelWithTableNames(NSArray anArray)
Pascal Robert 6.1 407 {
408 return new EOModel( this.defaultModelPath() );
409 }
410
411 {{/code}}
412
413 And voilà. No you have a toy file system adaptor (aka JavaFSAdaptor).
414
Pascal Robert 8.1 415 (% class="alternate" %)
Pascal Robert 7.1 416 * To "fetch" some files, simply create a qualifier with some path
Pascal Robert 8.1 417 attributes (eg absolutePath):
Pascal Robert 6.1 418
419 {{code}}
420
421 EOQualifier aPathQualifier = new EOKeyValueQualifier
422 ( "absolutePath", EOQualifier.QualifierOperatorEqual, System.getProperty
423 ( "user.home" ) );
424
425 {{/code}}
426
Pascal Robert 8.1 427 (% class="alternate" %)
428 * To get all the files in a directory, use the "parent" attribute:
Pascal Robert 6.1 429
430 {{code}}
431
Pascal Robert 7.1 432 EOQualifier aPathQualifier = new EOKeyValueQualifier( "parent",
Pascal Robert 6.1 433 EOQualifier.QualifierOperatorEqual, System.getProperty( "user.home" ) );
434
435 {{/code}}
436
Pascal Robert 7.1 437 Once you have your qualifier, simply build a fetch specification for it
Pascal Robert 8.1 438 and get your "file" EOs:
Pascal Robert 6.1 439
440 {{code}}
441
442 EOFetchSpecification aFetchSpecification = new EOFetchSpecification
443 ( "FSDirectory", aQualifier, null );
Pascal Robert 7.1 444 NSArray someObjects =
Pascal Robert 6.1 445 anEditingContext.objectsWithFetchSpecification( aFetchSpecification );
446
447 {{/code}}
448
Pascal Robert 7.1 449 You can use FSFile to retrieve files only. FSDirectory for, er,
Pascal Robert 8.1 450 directories. And FSItem for both. FSDirectory have two handy
451 relationships: "files" and "directories". Check the attached model.
Pascal Robert 6.1 452
Pascal Robert 8.1 453 (% class="alternate" %)
Pascal Robert 7.1 454 * To "create" a file (or directory), you can do something along those
Pascal Robert 8.1 455 lines:
Pascal Robert 6.1 456
457 {{code}}
458
Pascal Robert 7.1 459 EOClassDescription aClassDescription =
Pascal Robert 6.1 460 EOClassDescription.classDescriptionForEntityName( "FSDirectory" );
Pascal Robert 7.1 461 EOEnterpriseObject anObject =
462 aClassDescription.createInstanceWithEditingContext( anEditingContext,
Pascal Robert 6.1 463 null );
Pascal Robert 7.1 464
Pascal Robert 6.1 465 anObject.takeValueForKey
466 ( "/Users/rszwarc/tmp/JavaFSAdaptor/TestFSDirectory", "absolutePath" );
Pascal Robert 7.1 467
Pascal Robert 6.1 468 anEditingContext.insertObject( anObject );
Pascal Robert 7.1 469
Pascal Robert 6.1 470 anEditingContext.saveChanges();
471
472 {{/code}}
473
Pascal Robert 8.1 474 (% class="alternate" %)
475 * Finally, do delete a file:
Pascal Robert 6.1 476
477 {{code}}
478
479 anEditingContext.deleteObject( anObject );
480
481 anEditingContext.saveChanges();
482
483 {{/code}}