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 may have transcription errors.

My name is Eric Noaillot. I'm going to talk to you about Advanced EOF. 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. And 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. 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? 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. firm. You can do your application some other ways. You can start from another point. And you can also iterate. You can do a small part of your application this way, and then come back to the beginning, and redo another small part, and cycle. So it's just to give you some pointers, where do you go to write a new F application.

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-- even before using EOF, when you are at your data model, you're 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 explode 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 joins, and your database is going to be, uh, what is it you want? I need to do all these joins 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 than 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 two megabytes in size, every time to show a list of EOs on the screen, you're going to fetch for every one of those two megabyte 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 how to partition my model.

I'm going to take this example that we talk about, that I heard about before, about the person, the student, and the professor. It's nice. You separate that in a nice Neurotransitory, and you create one table for each. And you do nice joints all over the place. Well, if it's one layer deep, probably OK. Two layer deep, start getting 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 that and refetch them from the database because the database changed 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 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 EOF. So when you build your data model, you have to see what are the choices you have and which one you're going to pick.

The first, the easy way, you 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. Amen.

primary key, it's generating a unique number from your process ID, the port number you're using, and the time, and some of the data. It's building this 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 actually 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.

Or the last, the easy way is if you want to do something simple, is you can mark the primarity an attribute of your object and fill them yourself when the object is inserted in your editing context. Say, oh, there is no primarity 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 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 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 and try to find what this thing is doing, or writing a little test application trying to see what this thing is doing when one is changing the other.

You can also compare your relationships, the relationships you're building. We ship some examples of your model in WebObjects in the examples. Just compare the relationships you have with the relationships in the model. Try to think, do they fit the same way in your design? Is it the same kind of relationship?

And look at the relationship 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-JavaClient 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.

OK, so I have a small application that I actually build 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, it's 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. Hello. Wake up. OK, so that's a good start for a demo. There we go. I have it two times now. It's probably completely swapped out. OK, 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's part of a component. There is some priority, some state, some comments on this bug from some users. A user on the bug, a user on the comments, a user on the component. This kind of relationship, actually, this one is not the simplest one I can show you. There is 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 priority in your model, 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. 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 editing 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 editing context can have a pointer to this priority. Your bug has a pointer to this priority. But then you are in a shared editing 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 editing context of this session? or the editing 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 EU access level. You can say, I want the relationship named bugs, and grab it from the model, and do some stuff with it, qualifying a fetch, and something like this. If you don't have the relationship, well, you have to build it first, and it's harder. 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 thing, the first choice you make with your enterprise objects, probably knowing what is your enterprise object. What do you subclass? You have choices there. First choice is you can use a neogeneric record. A neogeneric 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 new generic record and forget about it.

You can subclass EOF if you want to add logic to it, or you can use a custom enterprise object that's actually a superclass of EOF to build more powerful full-fledged object. The choices of subclassing EOF or custom enterprise object is just a matter of taste, actually. In WebObjects 4.5, there was a huge performance improvement if you were subclassing EOF 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, usually, NeuroCritical, by default, is implementing a feature that's called deferred faulting. And Custom Enterprise Object, by default, don't implement deferred faulting.

Custom Enterprise Objects are actually my favorite, because I like to control everything. I'm a freak. I'm writing code. But EOGENERIC_RECORD just works. And actually, the first thing you do when you create an enterprise object is you go into your modular, you click the button, and generate the stub for the classes. And basically, you write your access source. This is a standard access source for something that's extending EOGENERIC_RECORD. You have something returning the title and something setting a title. You see that I'm still classing EOGENERIC_RECORD. 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 at 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, is 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 thing. Your EO is sitting in the middle.

And the other one is changing your object because the user wants to. What you don't want is to add any code when you're fetching the objects. 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 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 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 setTitle method to be called and change your modification. 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. Wait. It's name named. OK.

I'll use label next time. And then key value coding still is trying to find a method name, underscore name, underscore get name, and/or an attribute name, underscore name. As opposed to this-- the set value are doing the same thing, looking for a method named set name or the instance variable name, or after that. And there is an order in the way they're doing things. And the big difference between key value coding and stored key value coding is that the order is different. So-- We can show everything now. So imagine you have your object, 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 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, you know, not clean.

Think about the protected and public and private, because it's going to change the way your object 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 access, you should 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 access source in your object. And the second thing you usually put in your object are validation. When the user changes something in the interface, you want to make sure that the thing is changing is correct. So you write the validation method. There was multiple sort of validation method. The first one is just validating an attribute. So in this case, it's really bad. Validating an attribute name named, whatever. So you 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 codes 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, the 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 role. 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 valid for delete is actually a little different. It's not taking any values, just taking the object of the world. It's not returning anything. If it returns something, it's written nothing or throw 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. Because we've written 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 rule-- 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-- oh, there's something missing on all these methods. They all declare to throw NSValidationException.

ValidationException. 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 the 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.

Other method you can implement on your 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 enable 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. Away 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 UF, that are will change and will read. If you're using Eugenio Quick Record, 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 will read. And before setting a value, you call will change. that tell the e-auditing context 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. Thank you. Finally, keep your EOs clean. When you're writing your EOlogic, You can put your access code in there, your framework code in there.

But if you don't keep your object clean, if you put specific code, your access specific code, or your framework specific code, that means that you cannot use your logic anywhere else. And with all this Java client technology, maybe one day you will need to have them on the client side. And if you put your framework code in there, you will never be able to take this thing and move it to the client side.

I'll make one exception for NeoAccess. Also, the EO Utilities method that you can use to do some specific thing. You can use those because actually those are present in NeoAccess, but they are also in your 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 make the code using two different packages. But that's the only exception that's OK. That's the only place where you can use EU 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 a 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 EU 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 exceptions of shared editing context that should be completely transparent. So everything is the same editing context. You don't have to bother with an editing context.

You don't have to think about, 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, 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 comments here saying, type info, saying that, oh, in this NSArray, there is some bugs in there. So this is just interface logic that's used 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 pop-up 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. Your should not-- it's not because you have a Boolean in your 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 WebBuilder. 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 is 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 you, and your application is getting all this data there. So what's missing in there? You know, I have my interface logic, I have my geologic, and I think it's time to tell you a little story about where do I put the rest.

Hopefully the customer is not in the room. I will not say any names. But I've been to a customer site six months ago. And they had this nice application with tables all over the place in their WebObjects application with HTML table all over the place. Every-- the whole components, the whole page was one component. 6,000 line of code. Everything in there. Fetching, rearranging, you know, an array that they sort. They 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 some-- 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, the requirements change again. And that's approximately the time where I arrived to the customer site and looked 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 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-- 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 a nice concept.

Then I told them, that'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 a month after. They moved all the code. They put everything inside their EOs.

It's just, at that point, I was, you know, you can put code somewhere else. It's not because you have a component there and an EO there that you can put something in the middle. And then, remind me of this. of these classes I had when I was doing software engineering in school and saying, you know, you have this great thing coming from Smalltalk, it's called Model View Controller. You know? Well, this is exactly it.

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, right? The component is just a view. And you need a place to put your controller, right? Okay. 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. And I'm going to make it available to you so you can look at it. And I'm pretty 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 contacts you have all over the place, nested, not listed, 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 OFS, 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 all frameworks, just a set of objects, suppressing Java Longo objects, and not touching anything in the framework. It's just accessing EOF, EU access. 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, But if it's just in a subclass of NSObject-- not NSObject anymore, java.longo.object-- if it's 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 just to run it. I think I already compiled it, so I should just run. It's coming.

OK, so it's just a 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 on your test. So I'm going to log in as a power user on this thing.

That's a really complex application. So it's a really simple application. You have users. And what is this thing doing? OK. You have user. So you have the power user. They have a green thing. And you can edit a user. And it's just direct web 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 telling 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 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 the 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 go into-- 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 going to take a component. 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. So 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. Just the full name, take the two and concatenate. You take everything, you go up the tree, and you print the name with the slashes. And the rest is just access. So there is not really a lot of business logic in there. I think that 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 states and things like that. So I'm going to add probably more business logic in there. State, priority, the state is just-- it's not a new generic record, but just could be. 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 EO logic, because 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 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 EU. Nothing in there. 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 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. There is a method-- can I delete an object? And right now, returning true, because it's taking too much time to check. So I can actually remove the method. Edit users, just going to the next page, editing the user. A delete user is just creating another page, just deleting the user and passing it on another page. A 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. Thank you. So I have a controller. Basically, what is a controller? I don't like the name.

If somebody had 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's keeping an editing context, eventually creating a new editing context when you build one, and that's calling these utilities, local instance of object my use taking three kilometers long I made a smallest one and that's looking and unlocking your editing context and also asserting the looking looking is something you have to be aware of if you are writing the complete multi-threaded application if you are if you only have one thread in the request response loop in web objects you don't really care about about looking. 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. And I have a login controller that's just doing a fetch with a 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 controller that are controller 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 the fact that you have all these controllers and you can test them by writing simple tests-- I'm going to just 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 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 developer that, well, we have this huge thing. We need to add this feature. And it's just starting to act 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, he broke something. Well, 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-- we can go back to the slide. Where did I put my thing? Actually, I'm going to go blank. Just a little 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? Test? Yeah, test now. So we had tests running. We have 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 to look for one of their customer. 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 a demo to show to one of his 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 oh, yeah, and he wanted-- their current application, they have this field in there that they don't have in there. Well, basically, we wanted a completely different UI. So we were, 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 way. But actually, everything is in the controller. So all you have to do is actually wire stuff in Web Objects Builder, and that's it. And that's a really, really, really powerful thing.

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 be giving me feedback.

Don't guarantee that I'm going to read all the feedback, OK? 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.uf, there is this really nice page that 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 and 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, Web Object 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.