Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2001-613
$eventId
ID of event: wwdc2001
$eventContentId
ID of session without event part: 613
$eventShortId
Shortened ID of event: wwdc01
$year
Year of session: 2001
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC01 • Session 613

Advanced Enterprise Objects Framework (EOF)

WebObjects • 56:39

This is an in-depth exploration of the advanced features of Enterprise Objects Framework (EOF). This session covers shared editing contexts, deferred faults, and schema synchronization, as well as batch fetching, prefetching, and delete rules.

Speaker: Eric Noyau

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

My name is Eric Noyau. I'm going to talk to you about Advanced COF. I'm actually working in the field. I'm the manager for the high services team in France. That's in Europe. That's on the other side of the Atlantic. Eric Noyau, Director, Enterprise Objects Frameworkduino, I actually have a really hard session there because I'm just between direct-to-web and direct-to-Java client. So two technologies where you don't write code, you write rules, and it's all nice. And I'm going to talk to you about how to build an application writing all the code.

You know, I'm a developer, so I code. That's what I do for a living. So I'm going to talk to you about the code. Where do you put it? What kind of code do you write? And how do you build a WebObjects application? I'm going to bring you from the beginning to the end. Building a WebObjects application, you start from somewhere, you narrow the thing, and you go to the end.

I'm going to try to do that in a logical way, in a way you can build an application. It's not...

[Transcript missing]

So where do you start? Well, most of the cases, you start from your data. Your application, you want to manipulate some data. You already maybe have some data in the database you want to access. Or you're writing a complete new application.

and you want to create the complete database, put your data in it, and control everything. So the first thing you have to think about is, what is the data you're going to manipulate? What is your data model? Some people like doing nice graphics with plenty of boxes all over the place and nice lines between them.

Some other people just scramble some stuff on a piece of paper, and some other people just start to write code. I'm mostly in the last thing, but that's a bad thing. You don't want to start like this. You probably want to start by understanding your data model and by understanding where you're going.

So what are the pitfalls when you are doing your data model? What are the problems you can have with EOF when you... Just even before using EOF, when you are at your data model, you are doing your data model, what are the things you need to think about? First of all, your design, when you do a design and you expose things in tables all over the place, there is always performance implication.

If you explode everything and you have tables everywhere, well, every time you're going to do a fetch, you're going to do 60 joints, and your database is going to be, "Uh, what is it? Why do I need to do all these joints all over the place? It's hard." In some other ways, for example here, the blobs.

If you have a big chunk of data, and this big chunk of data are in the same table as some other data, every time you're going to fetch a Neo, you're going to fetch this blob. Well, if this blob is a nice image, and 2 megabytes in size, every time you want to show a list of Neo's on the screen, you're going to fetch for every one of those 2 megabytes data that you actually don't really need.

So it's actually easier to take this blob and to move it in a separate table, so you don't have to load it all the time. You just load it when you need it. Same thing with inheritance. Inheritance is nice. It's a nice way of thinking about, you know, to partition my memory. I'm going to take this example that we talked about, that I heard about before, about the person, the student, and the professor. It's nice, you separate that in a nice inheritance tree, and you create one table for each, and you do nice joints all over the place.

Well, if it's one layer deep, probably okay. Two layers deep? Hmm, it starts getting, you know, a little harder. If you go really deep, well, it's a huge performance impact. And you're going to suffer. Your application is going to suffer for that. So you probably want to coalesce some of these classes together to make some compromise in your design in order to get some more performances. But you have to think about this when you build your data model.

Triggers, stored procedure, we can deal with it with EOF. There is some way to call stored procedures. There is some way to deal with triggers. But those things are making your job harder. If you're writing a database from scratch, if you're writing your model from scratch, don't put any of these in there, except eventually for modification dates or creation dates. You can use triggers for these things. But otherwise, try to avoid those. If you have some in your model, you can deal with them.

For triggers, every time you save something and you know there is some triggers, you have to invalidate everything that's around them and refetch them from the database, because the database changes behind your back. Stored procedure is just a different kind of invocation, invoking something to get some data back. So you have to write some code to do the invocation of the stored procedure.

And on the data model at last but not least, you have to think about how your primary key are generated. Primary key are the core. Every time you create an object, you're going to need a primary key for it. And there is multiple ways to deal with primary keys with the EOF. So when you build your data model, you have to see what are the choices you have and which one you are going to pick.

The first, the easy way, is just let the adapter handle it. The GDB-C adapter knows how to do it. The different plugins can do things differently. But basically the adapter knows how to create a primary key. If you make a primary key in your model, any kind of integer, it's just going to work. In WebObjects 5, there are actually performance improvements in this area. Because the adapter is getting this thing in batches, and not just one by one. So if you insert a thousand objects, there is not a thousand run trips to the database. Just, I don't know.

Eric Noyau is a huge 24-byte binary thing, and that's it. And you save a primary key in the database. Sometimes, if you need to present a primary key to the user, that's definitely not a good choice. Of course. Exadecimal big primary key. Remember that. If you have some existing data, and the primary key generation in there is specific and doing some weird thing, taking actual current values that exist in the object and put them together, you can implement the delegate database context new primary key and just return a dictionary from there that will be the primary key of your object.

The easy way, if you want to do something simple, is you can mark the primality, an attribute of your object, and fill them yourself when the object is inserted in your editing context. Say, "Oh, there is no primality in there. Let's put something and get to something from wherever you want." So, you've built your data model, or you've thought about it, you know where all the data is going to be, or you're going to design the data, and now you have to build a model file.

You know all this magic in Direct-to-Web and Direct-to-Java Client is all happening because there is a model file, and just, there is rules extracting stuff from there. So you need a model file, and a model file is actually not that easy to get right. So my first tip on your model is, spend time on your new model. Just don't try to rush it, and just write on your model, and go write your application.

Just, you know, look at your new model and click on all the inspectors. If there is checkbox you don't understand in there, well, maybe you should understand it. And maybe you should look in the documentation, try to find what this thing is doing, or writing a little test application, trying to see what this thing is doing when it's changing the other.

Eric Noyau You can also compare your reactions. The reactions you're building. We ship some examples of your model in WebObjects in the examples. Just compare the reactions you have with the reactions in the model. Try to think how do they fit the same way in your design, is it the same kind of reaction chip? And look at the reaction chip and see in the inspector if they look similar. If something is different, try to think why it's different.

And finally, just take this model and use one of these great direct-to-something technologies. And just drop your model in there, run a direct-to-Java client application. If the application looks like something you can use, your model is probably okay. If your application looks like it's weird, there are entities that you don't expect there, but it's in a separate window, it should be in the same window, maybe you did not express the relationship exactly right. The heuristics in direct-to-Java clients particularly are really excellent and are giving you a good overview of your data model.

Okay, so I have a small application that I actually built most of it in the plane. It's nice to have a power book. It fits on the tray. So I built a small project, a small project, and inside this thing is yet another bug tracker system with a different database, a different model, a different everything.

So just everybody needs a bug tracker system, and everybody wants his own. It's opening. Double click on it. Go. Wake up. Okay, so that's a good start for a demo. There we go. I have it two times now. It's probably completely swapped out. Okay, so it's just a really simple model file.

OK. I'm going to make it bigger. You have a bug. The bug has some component. It is part of a component. There are some priorities, some state, some commands on this bug from some users. A user on the bug, a user on the commands, a user on the component.

This kind of reaction shape, actually, this one is the simplest one I can show you. There's something simpler. So it's a really simple model. But I just want to use this model to show you some little things. As you know, in a database, usually a little database of bugs exists.

you have something like five different priorities in your data, and you have maybe 10,000 bugs. So something you probably don't want, if you go look at priority here, is the relationship to bug should probably not be a class property. It should probably not be part of your priority object.

Because if it's part of your priority object, every time you add a new bug with this priority, well, we need to add this bug into the array of all the bugs of this priority. So you're going to fetch all these objects just in order to insert this thing into this array. And it's a costly fetch.

So this relationship, the inverse relationship is very important. The inverse relationship, from a bug, you want to know what the priority is just by following the pointer. But the other way, you probably don't want it. So the first reason is performance. You have this thing, and you have this huge array, and you don't want to fetch it.

The second reason is... The priority, you probably want to fetch it at the beginning when you launch your application. You want to fetch all the priorities, all the states, and this information that's standard for all the bugs, and you can modify. And you probably want to share this information by everybody, by every single leading context in your application.

Every single user in your application is going to use the same objects. And in order to do that, you use a shared leading context. So shared leading context is a place where you can put objects, and every other leading context in the application can use these objects directly. You only have one copy of these objects in memory.

What that means is your object in your own leading context can have a pointer to this priority. You know, your bug has a pointer to this priority. But then you are in a shared leading context. You have all these shared objects. And imagine that you have a pointer to all the bugs.

Well, all the bugs where? All the bugs in the leading context of this session or the leading context of this session? You don't have the information. So you cannot share an object that has a relationship to data that's not shared. So it's a one-way. You have a one-way point direction going from your objects into a shared object. But from the shared object, you cannot go out to an object that's not shared. So that's why this thing is not a class property.

I still like to put the relationship in the model though, because it's actually useful information that's something you can actually use when you're programming at the EEO access level. You can say, "I want the relationship named 'Bugs'," and grab it from the model and do some stuff with it, you know, qualifying a fetch and something like this. If you don't have the relationship, well, you have to build it first and its order. It's nice to have it in the model file. Let's go back to the slide. So what I'm going to say on this model is really simple.

So, once you've built your model file, you have to think about your logic. Where are you going to put your logic? What kind of code do you write? You have plenty of code to write in your application, and some of them are more database code, some of them are more UI code, some of them are somewhere in the middle.

So I'm going to review the different class of code you have to write in your application, and where do you put it. And I'm going to start with-- something I call EO logic. So it's basically everything that's on your enterprise objects. The first choice you make with an enterprise object is probably knowing what is your enterprise object? Why do you subclass? You have choices there.

First choice is you can use a Neo Generic Record. A Neo Generic Record is just-- it's just a dictionary. It's just something you have some keys, some values, and it's just somewhere where you put your data. There is no code, no logic. It's just your data. So you just don't put code on this. It's useful for some little objects where you don't want to put some logic on it. For example, my priority object, probably I don't want to put code on it. I can just make it a Neo Generic Record and forget about it.

You can subclass EOG if you want to add logic to it, or you can use a custom Enterprise Object that's actually a superclass of EOG to build more powerful full-fledged Objects. The choices of subclassing EOG or custom Enterprise Objects is just a matter of taste, actually. In WebObjects 4.5, there was a huge performance improvement if you were subclassing EOG for long reasons.

But in WebObjects 5, you don't really care. You can subclass one or the other. The only difference between the two is EOG by default is implementing a feature that's called deferred faulting, and custom Enterprise Objects by default don't implement deferred faulting. Custom Enterprise Objects are actually my favorite because I like to control everything. You know, I'm a freak. I'm writing code.

But EOG just works. And actually, the first thing you do when you create an Enterprise Object is you go into your modeler, you click the button and generate the stub for the classes, and basically you write your accessors. This is a standard accessor for something that's extending EOG. You have something returning a title and something setting a title. You see that I'm subclassing EOG, and I have this weird code to store value for key, take stored value for key. I'll get into that in a minute.

Because this is the way the framework is accessing your object. And you have to understand how the framework is accessing your object to know where to put your code. Because if you don't put your code in the right place, the framework is not going to invoke it the way you want it. So, accessors are really important.

You have the accessor to set the value and the accessor to get the value. And you can put code in there. If somebody tells you that you cannot put code in an accessor, it's going to break or lose, that's not true. You can put code in there. But you have to be really careful when you put code in there. It's the story.

And why do you have to be careful? Because key value coding is actually relatively complex. You have two ways of accessing your EOs. And the frameworks we have are accessing the EOs in these two different ways. If you imagine a stack. You have your database at the bottom and then you access and then you control and then the interface and web objects or any kind of Java, Client thing. Your EO is sitting in the middle.

The other one is changing your object because the user wants to. What you don't want is to put some code in the... to add any code when you're fetching the object. Probably not a good idea, because when you're fetching a bunch of objects with some performance, if you put some code there for every object you fetch, you're going to touch this code and repeat this thing over and over. So stored value for key, usually you don't put code in there.

On the other side, when you use key value coding, when somebody is invoking your object via key-value coding, and not stored with key-value coding, you may do something. Like, let's take a simple example. You have a modification date in your object. And when somebody changes the title of a bug, it's changing the modification date on the set title. If you do set title on your EO, it's going to say the modification date equals now, and put that into your EO.

So changing the title is actually changing two attributes. But when you fetch the object from the database, you don't want this set title method to be code and change your modification date. You're not modifying the object. You're just fetching it. So you have to understand how key value coding works and how key value coding invokes the things in your object to know where to put your code.

So the first thing is, key value coding, the user interface, is accessing your object. What is it calling? In what order? So the first thing key value coding tries to find is a method with the name of your attribute. You know, name. Actually, one that's missing on this slide, everywhere, everywhere where you see name, you can replace that by get name.

We're in Java, so we support the get version also. So it's looking for a method name, get name or name. And then it's looking for the instance variable directly, trying to access it and see if I can put something into this instance variable name, name. That's a bad name for an attribute to explain. Well, it's name, name, okay.

I'll use "label" next time. And then, Key ValueCoding still is trying to find a method name, _name, _getName, or an attribute name _name. As opposed to this,

[Transcript missing]

We can show everything now. So imagine you have your objects, and you have two methods on it. One name, name, and the other one name, underscore name. If you are in key value coding, the one that's going to be called is name. If you are in start key value coding, that's going to be underscore name. And you can play with this thing. You can turn them around.

Something important in WebObjects 5 is it's pure Java. So all these methods need to be public. If you want key value coding to access your instance variable directly, you better make it public. If your object is not in a package, we'll find it. We have ways to find it.

But if you put your object inside a package and you make your instance variable protected, WebObjects has no way to access this thing. When we were in Objective-C with the bridge and stuff, we were going from under you and just putting the value from there. But we can't do that anymore. We don't want to add some stuff in the VM to do that. It's just not clean.

Think about the protected and public and private, because it's going to change the way your objects are accessed if they are in a package. If they are not in a package, don't bother about it. It's just going to work like before, except if you mark them private so nobody can access them. But if they are in a package and protected-- not in a package and protected, we can still access them.

You can change the way key-value coding is working by implementing one of those methods. Those are static methods, but we do some magic so they are inherited. So you can inherit this classic method. So you can say, "I don't want the frameworks to access my field directly." "I just want the framework to use the methods, never the field." And should you use stored accessor, if you return no to this method, there is no difference between key-value coding and stored key-value coding. They all do exactly the key-value coding thing. There is no more stored key-value coding.

So you have your accessorize in your object, and the second thing you usually put in your object is validation. When the user changes something in the interface, you want to make sure that the thing is changing is correct. So you write a validation method. There is multiple sort of validation method. The first one is just validating an attribute. So in this case, it's really bad, validating an attribute named, whatever.

So just write a method, validate name, taking a string, because my name is a string, and returning a string. And this thing is throwing an exception. And you just do whatever you want to check on this particular attribute, and return the name if it's fine, and throw an exception. And it's validation exception, if there is any problem with the name. So here, I don't want-- the name of a component should contain more than four characters, just a random rule that I made up.

These methods are actually called automatically when you save your object. There is something that's running around and checking all these things. These are also called if you use WebObjects, when you bind with WebObjects. WebObjects is actually calling these methods. If you use a display group, display group is also calling these methods. But when you save, it's called anyway, so you're sure that this thing can go to the database without being checked.

Second kind of validation method is validation method that takes your object as a whole. So when you delete an object, here I'm just doing something, it's not really interesting validation, but I'm just checking that I'm still in the component, and my component could have sub-components, and I just want to make sure that you cannot remove a parent, you have to remove all the child first. So you cannot suppress a component name if you have something. So this validation for delete is actually a little different, it's not taking any values, it's just checking your object as a whole. It's not returning anything, if it returns something, it returns nothing or throws an exception.

So, validation. Validate value, taking the value. Take a value, return a value, and throw an exception. That's important because in Java, a method signature is actually the name of the method and the type of its arguments. The type of the return value is not part of the signature.

So when we find a validate method, we don't know what this thing is returning. So if you write a validate method that's returning void, we're not going to notice, and you're not returning anything, so we're going to put null in your object. So it's returned void, we don't know where to find this thing, we put null. And there is no really easy way to get out of it. So make sure that you return something from a validate attribute method.

The three methods for violating your object as a role: Validate for delete, Validate for insert, Validate for save. I think they are self-explanatory. By default, Validate for insert and Validate for save are doing the same thing. In these three methods, there is something missing on all these methods. They all throw, they all declare to throw NSValidationException, validation exception. In these three methods, something that's important is to call super.

Because if you don't call super, the validation that's set in your model file is not going to happen. Because this is invalidated for save, that the framework is actually going through all your attributes and saying validate name, validate title, validate something, validate something else. So you have to call super. If you don't call super, all the validations that you have set in validate something is not going to be called. And all the validation that's in your model file is not going to be called either. So that's a pitfall to avoid. Yeah.

Other methods you can implement on your EO logic. Enable to set null for key. If you have an attribute that's an integer, we cannot put null in there. So if we can't put null, we're just throwing an exception. If you're unable to set null for key, you can do You can do stuff there and we're not going to throw if we implement this method.

A work from insertion, when you insert a new object into an editing context, that's actually a place, when you create a new object, the first thing you do, you insert it. When you insert a subject, there may be some stuff you want to put some default value in there, you want to set up a creation date. This is actually a method that's really useful that you can use all the time.

Away from fetch is once you fetch from the database, the framework is going to call you and tell you, "Hey, you've just been restored from the database. You can set up some value in you and do some stuff." Methods you should probably not override are the two methods that are used by IoF, that are WillChange and WillRead. If you're using Eugenio QuickCore, you never see those methods, just subclass and call stored, value for key, and calling automatically, these two methods.

If you're implementing custom objects, you see these two methods. Before accessing an accessor, you call WillRead, and before setting a value, you call WillChange. These methods tell the IoEditing contexts looking at your object that something is about to change, or we need to fetch data because it's going to need this data.

These methods are called really, really, really, really often. Every time you access an attribute, every time you set an attribute, you call one or two of them. So those are expensive methods used all over the place. If you put code in there, and this code takes a little while to run because you want to do some, I don't know, something, You're going to have a performance problem right there. Because your fetch, you can easily double the time you're taking to fetch a set of objects from the database by implementing three lines of code in there. So that's why this method, you probably should not override them.

Finally, keep your EOs clean when you're writing your EOlogic.

[Transcript missing]

I'll make one exception for EO Access. Also, EO Utilities method that you can use to do some specific thing. You can use those because actually those are present in EO Access, but they are also in EO Distribution in a different package.

So you can actually use them in both places, but you have to be careful by the way you use them. If you use the same code in both places, you can't, obviously. So you have to subclass and mix the code using two different packages. But that's the only exception that's OK. You can use-- that's the only place where you can use EO Access specific code.

So that's the first part of your code, the code you're writing. This is your accessor, your validation, and your business rules. Business rules are just the code that you need on your EOs, just going from one EO to another and doing-- I want to buy a car. I have the car object. I have the buyer. And I do all this thing that's part of the business. That's part of the thing you do.

so these are the business for you can also implement in there In the EO logic, you can assume that everything is in the same editing context. That's an important assertion because all your objects are tied together, they are all in the same editing context, with the exception of the shared editing context that should be completely transparent.

So everything is in the same editing context, so you don't have to bother with an editing context, you don't have to think about, "I'm in this one, this other one..." No, you're all in the same editing context, and the first thing you do when you create one of these objects, you put it in an editing context, so you know it's in there, and you don't have to think about it. This is not the place where you create new editing contexts, or where you move things from one to the other. It's just, everything is in one editing context, and you just write your logic in there.

So, that's the first part of the code you're writing. The second part, you probably expect that most of you are familiar with this. This is your interface logic. You know, the thing you use to connect in WebObjectsBuilder. This is classic thing. You have an array of bug, and you have a current bug, and you build a whole repetition over it.

And you have the little comment here saying, type info saying, "Oh, in this NSArray there is some bugs in there." So this is just interface logic that you use to connect in WebObjectsBuilder when you build your application. Nothing really related to EOF in there. It's just you have some arrays of objects, and you connect them.

You have the inside of an object contain a Boolean value, and you want to present a popup with yes/no. That's also something that you do in your interface logic. Just transformation of your logic from one type to another just for display purpose. So this goes to the interface logic. This should not go into your EO.

Your EO should not, you know, it's not because you have a Boolean in your EO that your Boolean should return yes or no as a string. It should return a Boolean, not a string. The string translation from the Boolean to the string should be done in the UI.

So I'll do it rapidly for the interface logic. The interface logic, you have everything to connect in Web Builder. You have all your translation from Boolean to strings. You also have, in your interface logic, everything that's related to the session. Classical session is the current user, who's connected to the application. That's a user you need when you invoke your business logic on your EOs.

When you have a user connected to your database, and the user say, OK, I want to buy this piece of hardware that you're selling in your catalog, you want to invoke the business logic with this user. I want to say, this user wants to buy this thing, or this user's shopping cart, I want to add this in it.

So this is in the session, and this is part of your interface. This is part of your application. This is your application keeping this state, not your EO. You're just manipulating EO, and your application application is getting all this data there. So, what's missing in there? You know, I have my interface logic, I have my EO logic, and I think it's time to tell you a little story about where do I put the rest.

[Transcript missing]

the whole page was one component, 6,000 lines of code. Everything in there, fetching, rearranging, you know, and arrays that were sorting, everything was in there. And this was a fairly complex page with sorting all over the place. You know, you click and you sort, and really hard.

And of course, they called me because of one, some performance problem. And the second thing is, like everybody else, the requirements change. And they needed the page in a slightly different way. And it took them six weeks to do the page in a slightly different way. And of course, when we are done, well, the requirements change again.

And that's approximately the time where I arrived to the customer site and look at the code, and it was all monolithic, all in one page. You don't want that. You don't want to put all your application, everything that you do, everything you fetch, everything that you sort, everything, all these things, you don't want to put that in your component, right? Because it's too much.

Even if you, the people, some people are subclassing components, are making components and subclassing the component and then subclassing the component and then reusing some part of the code. That's already better. That's already way nicer because you can actually reuse some of the code that's actually some nice concept.

Then I told them that it's not a good place to put these things. And I just went there for a day, because I'm too expensive, and they can't have me for two days. So I just went there for a day, fixed some bugs here and there, and tell them, your thing is completely monolithic. You don't want to put all your code in your component. Put it somewhere else. came back to the customer months after, they moved all the code, they put everything inside their EOs.

[Transcript missing]

The logic, your EO logic, that's your model, right? You have the interface logic, it's where you put your interface, that's your view. The components are just your view. And you need a place to put your controller. So, what I'd like to suggest is that you think about this.

I wrote a little application there that I want to show you after. But, I'm going to make it available to you so you can look at it and I'm interested by the feedback. So where do you put the rest? You put that in a controller layer where you can have everything.

So that's your application logic. That's where you fetch, you save, you eventually cache some information because you know that you're going to need it at some other place. This is the place where you deal with all these editing contexts you have all over the place, nested, not nested, and all these things.

So you can support a back button that's working nicely. This is the place where you do all your prefetching, where everything that you do with the OAFs, that's basically, you want to do that in a controller. You don't want to do that in your model and you don't want to do that in your interface.

I'm going to save my other story for the end. So what are the advantages of having a controller that's ideally not dependent on frameworks, just a set of objects, replacing Javaongo objects, and not touching anything in the frameworks, just accessing UoF, uof, uaccess. And you have methods in there to build fetch specification, to retrieve fetch specification, to retrieve a set of objects.

Well, you can reuse them on the client side. All this logic that potentially is in a component or in some other place. If it's just in a subclass of NSObject, no NSObject anymore, Javaongo object, if it's in a subclass of object, you can reuse it. You can move it to the client side. And it's a subclass of object, so actually you can test it without running your WebObjects application. You don't have to have a woe component, a woe session, or something. You can just write some three lines of code and test this thing.

So what I want to show you now is a little... if I can have this screen on... It's this little application I wrote while I was on the plane. So I have here an application that's just a little simple application. It's a bug tracker system. I'm going to just run it. I think I already compiled it, so I should just run. It's coming.

Okay, so it's just simple application where you can log in and search for bugs and, oh, run tests. That's actually something that's really interesting, is having all the tests available from the user interface when you're in development mode, and you click on the button and it's running all your tests. So I'm going to log in as a power user on this thing.

[Transcript missing]

And what is this thing doing? You have user, so you have the power user. They have a green thing. And you can edit a user. And you know, it's just direct web, but with code. Plenty of line of code to do all this. You can sort, of course. Reverse sort and all these kind of things. Can go look at the component that exists in my database. So you see that I'm submitting only bugs for bug tracker. and I can create new bugs.

So I wanted to do an application that was not just a demo, but something that actually worked. This application is fully functional except for the search page because I didn't have time to finish it. I fell asleep in the plane. So everything else is functional. That's why I'm not clicking on the search button because it's embarrassing.

But the idea is if you try to save an empty bug, it's going to scream at you and tell you that you have to write this, you write this, you write this. So all the validation things are running and I'm showing the right thing at the right place.

So, how do I do that? It's actually... Here we go. So I split the application in four parts. So we have our EO logic, our UI logic, and we have our application logic, and I also have one test thing where I write... where I put all my tests. Actually, I want to run the test because it's fun.

I like running the test. That's a good exercise. At the customer side, we actually have the test. You launch the application. It's running all the tests. And if one of the tests fail, you cannot launch the application. You stop right there with the error. And you cannot go further.

So you need to fix the bug before you go further. And actually, we don't have any bug in the application so far, because every time we find a bug, we make a new test. And you cannot launch the application until you fix a bug. So guess what? It's fixing the day.

So this is just a simple thing that's running all my tests. And if there was an error-- maybe I should have done a test with an error. It could be fun. You have a back trace there. And you click on this. And you can see what line it is in your file and fix it right away.

The EOlogic. There is nothing really in there. It's just, you know, going to take a component. The component is simple. If I close this so we can see the code in there. So a component has a validate for delete method that checks that you cannot suppress a component if you have subcomponent for it. You cannot suppress a component if there is bugs attached to this component.

There is also the validate name method that I was looking for something. So this is the one I put on the slide. Just a simple validation method. We also have the really big, nice business logic here of asking the full name, take the two and concatate. You take everything, you go up the tree, and you print the name with slashes. And the rest is just access. So there is not really a lot of business logic in there.

I think that in... I'm going to advance in this application because I think I'm going to finish it now. I'm going to add something to move... to change the state and things like that. So I'm going to add probably more business logic in there. State, priority, you know, the state is just... it's not a new generic record but just could be. There is no need for it.

The only thing nice about the state is there is access also. You can just use them. And then there is two classes in there that I put in my EOlogic because, you know, I didn't know where to put them. And they are actually really useful. The first one is for the shared editing context. When you have, you know, you have a set of objects and then you fetch all your priorities in there. And then you add a new priority. And you want this thing to appear magically in your shared editing context. So that's what this subject is doing.

I'll let you look at the code because it's maybe a little too long to explain here. And I have another little class here that I use all over the place. That's just bringing me a static method thing. Object count with fetch specification and returning me a number. So I don't have to redo it all over the place. So that's basically my EO. Nothing in there. Really, really, really simple.

My user interface: I have a bunch of pages: the main page, the component list page, the component editor page, user list, user editor, bug list, bug editor. When I have an editor, I'm using it for edit and also for new objects. So this is just, you know, all the UI, and when you look at one of these objects, let's go look at the user list in there, it's just UI glue, there is really not a lot of interesting things in there. Yeah, there is a method "can I delete an object?" and right now it's returning true because it's taking too much time to check. So I can actually remove the method.

Edit user is just going to the next page, deleting the user. Delete user is just creating another page, just deleting the user and passing it on another page. New user is just going to another page and creating a user. But if you look at this code in there, I'm never touching a user. Never. All I'm using are user controllers.

See, Edit User, what I do is I create a page, the User Editor, and I create a new User Controller with the current user. That is the current user, that's the one that's in my repetition, so wherever I clicked. And in the next page I tell it, set the User Controller, this User Controller, and go do your job. And all the logic is in the User Controller. What is the User Controller? Well, that's my third bucket. That's in there.

So I have a controller. Basically what is a controller? It's something... I don't like the name. If somebody has a good suggestion for a name... It's a model view controller, but I don't like the name controller, so it may be a little difficult. But a controller is basically a shell on top of an editing context.

It's something that keeps an editing context, eventually creating a new editing context when you build one. And that's calling this "EO Utilities Local Instance of Object MyEO" taking 3 kilometers long. I made the smallest one. And that's locking and unlocking your editing context, and also asserting the locking.

Locking is something you have to be aware of if you are writing a complete multi-threaded application. If you only have one thread in the request response loop in WebObjects, you don't really care about locking. But it's nice to see how it's done. That's why I put it in there. So you can see how you can lock and unlock your editing context, and having something that's running in all the cases nicely.

So let's see what I have as a controller. I have a login controller that's just doing fetch with fetch specification to get the current user and returning some error messages. I have some simple controller. What is a simple controller? That's a controller that's basically taking care of one object. When I was looking at the delete user, I was creating a controller with the current user and telling it to delete. That's basically a controller for this one object. I can also create, I also have list controllers that are using a list of things.

And finally, it's a demo, so first time you launch the application, if the thing is empty, it's filling the database with data, fake data. And then I have all my tests. I want to insist on this because this is actually something that saved our butt a couple of times. Because, you know, if you don't have the tests, the fact that you have all these controllers and you can test them by writing simple tests. I'm going to rush to write, show you the login test in there.

You know, test login is just creating a login controller, set the username, set the password, and ask it for the user. And if this thing blows up, the user is not there. So I'm just asserting that this thing is doing the right errors at the right time. And doing multiple things like this, testing multiple cases.

This is really nice because imagine you have one of your developers that, "Well, we have this huge thing, we need to add this feature." And it's just starting to act in the right way. And you go into the code and write all this thing and commit it into CVS and that's it. That's finished. And then two weeks later, you notice that, well, you broke something.

If you have tests, testing your controllers in all the different ways, if you break something, you're going to notice right away. And actually, This happens more often than you can think, too. It's something that's happening really, really often. So I guess there is-- where am I? I guess I'm there. I just want to finish with... if we can go back to the slide... where did I put my thing? Actually, I'm going to go blank.

Just a little short story to tell you why I really think this thing is good. At another customer site, these ones are doing these things right because they hired me to manage the project. So at this customer site, we started building an application, and we used these concepts all the way.

And I told them, "You write tests before you write your code." "What? Tests? Yeah, tests, now!" So we had tests running, we had everything running, it's all nice. And then the CEO of the company wanted to show a demo of this application, but he wanted to customize it.

Because the look we were using was the look for one of their customers. And this is an application that has a different look. They have people going to the customer site and customizing these applications to the different sites, so they look different. So he wanted this application that we started from scratch, something like three months before.

He wanted the demo to show to one of these customers. Of course, we didn't have anything ready. It was just a short demo. We had something running, but it was with the logo of somebody else. So he wanted it with the logo of the real person. And he wanted their current application, they have this field in there that they don't have in there. Well, basically, he wanted a completely different UI.

So we were like, well, we cannot do that. And then we went for it. We just, you know, we have to do it. So let's do this UI. So we went in there and removed the UI and started from scratch with the UI. And basically, we did the demo.

And we had to write something like six lines of code. Because everything was in the controller. And I didn't even think myself that it was going to work that well. But actually, everything is in the controller. So all you have to do is actually wire stuff in WebObjects Builder. And that's it. And that's a really, really, really powerful thing. So.

I don't have all the answers. There is some stuff in this application I'm not happy with. There is some stuff I'm really glad I did it this way, some stuff I don't really like. That's why I didn't finish the search, because I'm not sure that the search I did is great. But even if it's a work in progress, I want you to look at it.

I want you to give me feedback. I don't guarantee that I'm going to read all the feedback, okay? I'm probably going to read it, but I'm probably not going to answer, because I don't know how many of you are in this room. But if you all send me 10 emails, I won't be able to reply. But I'm still interested in the feedback. If you can go back to this screen for a minute.

I've used some powerful Apple technology in there. So if you go on homepage.mac.com/EOF, there is this really nice page, as I did with PageMaker, where you can download this exact application that I just showed you. And feel free to send me feedback. Feel free to send me any comments that you have. I'll try to integrate them if I can. Send me flames if you want. I read them too. Okay, back to the slides. And that's basically all I had to say today.

As usual, there is a lab downstairs, 10:30, 3:30 tomorrow. Go vote for WebObjects. There is a direct to... advanced direct to Java client just after this session. You can... and some other session with project security. I'm sure it's going to talk fast in there. And the feedback forum. Don't miss the feedback forum. We want your feedback. And who to contact? And I think this, Bob.