-
OK, welcome to the best practices session.
-
I'm Chuck Hill
-
I work for Global Village Consulting.
-
It's a WebObjects consulting house based out of Vancouver, Canada
-
I'm fairly active on the mailing list
-
so many of you will probably know my name.
-
You may have heard it once or twice before.
-
I'm also the coauthor of Practical WebObjects.
-
If you don't have a copy of the book, you probably want one.
-
Thank you, thank you, thank you.
-
OK, so this started out being called WebObjects Best Practices Sessions,
-
but as I looked at them and I talked to some of the other illustrious experts
-
it became obvious that we didn't all agree on exactly what the best practice was.
-
I decided to waffle a little bit, and it's Quite Good Practices.
-
My intention as going through here
-
is I'll present some best practices
-
and I'll give some members of the panel here
-
the opportunity to heckle me
-
and tell me that my best practices aren't really very good at all
-
and give you some ideas of theirs and what might be better.
-
In fact, some members of the panel
-
may argue that there is only a single best practice
-
which is use Project Wonder.
-
I won't tell you that.
-
I'm not going to assume you're going to be using Project Wonder
-
but I'll mention a few really cool things that are Project Wonder that you probably want to use.
-
Keep in mind also that this is a what session
-
it's not a how session.
-
I'm not going to tell you how to implement all of these things.
-
There's the wiki, there's the mailing list.
-
There are lots of ways to figure out how to implement this.
-
At the end hopefully we'll have some time for some questions and answers.
-
Some topic areas.
-
The first topic area, I divided it up into four areas, is going to be WebObjects.
-
What we're going to focus on here is the presentation layer
-
not the whole WebObjects framework but just components
-
talking about getting things out to a viewer.
-
The next thing is EOF,
-
so we're going to look at some enterprise object‑related,
-
database‑related things that are best practices.
-
There's just a few things that are pretty much generically Java
-
but they have a particular application to WebObjects,
-
so we'll cover those in Java.
-
We'll finish up with a few deployment best practices.
-
The whole point of having best practices
-
is so that your application doesn't end up
-
looking like what these two gentlemen are standing on.
-
I have, over the years,
-
maybe written some applications like this a long time ago.
-
I've inherited a few of them.
-
Trust me, you don't want one.
-
OK. On to our WebObjects presentation layer of things.
-
We're going to talk a little bit about page inheritance.
-
Now, WebObjects is pretty good,
-
and you can use it out of the box and you can get a lot of value out of it.
-
That's what people usually start doing...
-
everything is WOComponent and then my page.
-
Judging by all the people who've written frameworks,
-
you're probably going to find out pretty soon that you've got a lot of common code all of your pages
-
One of the things that you should do is create a common page.
-
Now, this is just a Java class.
-
There's no WO file. There's no template involved in it.
-
It's just common Java things, takes the good stuff that comes from WebObject's WOComponent
-
and adds some more good things to it.
-
Then, search results here, as an example of a page you might write,
-
which inherits from common page.
-
It's a way of getting a lot of benefit on your pages without having duplicated code all over the place.
-
Let's take a quick look at some of the things you might want to do in this common class.
-
Enhance WebObjects with editing context,
-
this is something important, and we're going to keep coming back to this again and again
-
If you're writing your pages using "session.default" editing context,
-
you're not using abstraction correctly.
-
You're programming to an implementation of something.
-
Sooner or later, you're going to want to change that,
-
and you're going to have a lot of code in a lot of places.
-
Putting something like that on your page
-
and having everything deal with that allows you to change it a lot more easy.
-
You'll see that it'll make your pages a lot more flexible.
-
Binding support, WebObjects has pretty good binding support.
-
Does it have a binding? Give me the value for the binding.
-
You can do a lot of useful things, though.
-
Casting bindings, give me the string value for the binding.
-
Give me a Boolean binding.
-
Give me this Boolean binding with a default of True.
-
Access control,
-
most of the apps you're going to end up writing are probably not going to be completely public
-
like the Twitter example where anybody can see anything and we don't really care.
-
By putting the access control in the page superclass,
-
you make sure that it's handled consistently all across the application.
-
You don't have to remember, "Oh, I've got to do it in this page.
-
Oh, I have to do it in this page.
-
You do it once, and it's handled consistently across.
-
KVC extensions, I like to play with KBC extensions.
-
You can do a lot of interesting things overriding key‑value coding
-
besides what it normally does.
-
One of the things that I've done recently is to make bindings like this.
-
When the page sees that, it takes it around and it ends up calling a session method.
-
With [indecipherable] in the session, can the current user...?
-
Do they have Create Users permission or not?
-
It ties the bindings directly into the authorization system.
-
You don't actually need the "@" in front.
-
You could just use Can User, but when I do extensions,
-
I usually put a little character in there to remind people that,
-
"Hey, this one's special."
-
The best way to learn things to do in this is to look at what other people have done.
-
ERX component, which Mike mentioned, was just added this year to Wonder.
-
It's a great source of ideas for the things that you can do in your own class.
-
As I think, Andrew has a component in his class.
-
Pierre has some in the How to Frameworks. Lots of good examples out there.
-
Yeah. I wasn't going to pump my own self, though.
-
I'm trying to let those go away.
-
There are lots of good examples out there. Take a look, get inspired.
-
Any comments from the peanut gallery?
-
- Yes.
- Yes?
-
Access control is something I forgot to mention. It will probably also come to your attention.
-
- OK.
-
- That's all.
- With a cool KVC extension?
-
- Yes, actually.
- Just one thing on the KVC extension.
-
If you're running you try to have a look at the name space.
-
There's a very, very cool feature in the [indecipherable] where we extend the binding property.
-
Actually you are [indecipherable].
-
Have a look at the name space and the binding.
-
It's a really, really cool feature.
-
OK. Now next thing I want to look at is component inheritance.
-
Basically taking the same idea we had before,
-
little component, we have a common page and a common class in a search results,
-
and saying "Well, OK, here's what the page does
-
but for components that appear in the page we want to make things a little bit different."
-
I've introduced the idea of a common component class, just pick the name.
-
As an example a result list is going to be a component that you would use in a page.
-
For example, the result list on the search results page.
-
Why do this?
-
Validation handling is one.
Who is it now?
-
I think Andrew's thing was showing how the components...
-
what's the word I'm looking for? Cooperated with the page, passing the validation messages around.
-
Instead of having one big block at the top that said
-
"You hit saved changes and this bad stuff happened"
-
you can have the error appear beside each component where it applies.
-
This is an important one.
-
Going back before we said each page should have the concept of an editing context,
-
each component shouldn't have the concept of an editing context.
-
Each component should use the page's editing context.
-
If you ever change the page's editing context,
-
that flows all the way down through the page.
-
If you have "session.default" editing context buried everywhere,
-
you've got a lot of changes to make.
-
That's a very simple way of how you do it, just put that in there.
-
Get the context page, it will give you whatever the top level page is.
-
Cast it to your generic class,
-
and that's what you should be using for an editing context.
-
- Interesting.
- You don't do that?
-
- I don't do that right now.
- Oh my...
-
- You learned something today.
-I learned something. Thank you [indecipherable]
-
- Yes?
-
- Hello? That's a great idea, Chuck.
-
I'm going to start doing that.
-
OK, and taking the whole idea one more step further, and this one's pretty simple.
-
The stuff up above the common component, that's pretty generic code.
-
But the problem with Java is in the WebObjects framework
-
the component method's application and session are cast to return a WO session and WO application.
-
Now usually you've extended them. You've got some of your own stuff in them,
-
so all through your pages you end up casting it and casting it.
-
They usually include a couple pages like that in the application
-
just strictly for the point of casting convenience.
-
That's not going the way it's supposed to.
Like that.
-
The whole point is override the method and cast it like that.
-
You can only do this with Java 1.5.
-
If you're still using Java 1.4, the compiler will whine at you.
-
You have to make a method with a different name like "the session."
-
Same idea for the application.
-
Any fun generic stuff Mike?
No?
-
Also great.
-
I've got nothing.
-
OK. Now I want to talk a little bit more about editing context.
-
- Actually I do have...are you going to cover the page with name?
-
- Yes.
- OK. Nothing more about generics right now.
-
OK, so here is how most people start out writing web applications.
-
How I started out writing Web Objects applications.
-
Session default editing context, all over the place.
-
This is how it usually works out.
-
You've got one session, one editing context.
-
Every page is tied to the editing context,
-
and the editing context is tied to the objects store.
-
Web Objects has a lot of methods that say defaults.
-
It's got a default modeling group,
-
a default shared editing context,
-
a default editing context.
-
A lot of these aren't very good defaults.
-
They're good to get you going,
-
but they're not something you want to be using long term.
-
Let's take a look at some of the problems that you're going to get in with this.
-
The dirty sandbox syndrome.
-
You go outside to play in your sandbox,
-
and the dog's been using it as a toilet.
-
The same thing happens with your editing context.
-
The user's in there, they get a validation error, they get cheesed off.
-
They go to the menu, they click some place else, they do something else,
-
and then they get the same validation error.
-
Because now the editing context is stuck. It's dirty.
-
You can avoid this. You can call editing context revert.
-
But you have to be pretty careful to do it,
-
because you never know when the user is going to back up,
-
and then they click the menu a bit.
-
If you don't call at the right time then their session gets stuck,
-
and they have to log out and log back in again.
-
That's the number one reason to avoid using a session default editing context.
-
Another reason is data freshness.
-
There's a concept of a default lag, a default fetch lag and a default timestamp in the editing context.
-
When you bring data into the editing context
-
it says, OK, I really only want data that's older than this.
-
Now, if you take Web Objects out of the box,
-
that's an hour before the user logged in.
-
If the user's been using your app for a half an hour,
-
they could be looking at data that's an hour and a half old.
-
Unless you've been doing refreshing fetches. I hate that phrase.
-
That's another problem with the default editing context, is it's as old as the session, and so is the data.
-
Excess data. This was really a problem, historically. It's not as bad now.
-
But the more objects you have in your editing context,
-
the more chances are related to each other,
-
and there's a greater chance that stuff's not going to go to scope.
-
It's not going to get garbage collected.
-
You're going to have a bigger memory footprint.
-
The best practice here,
-
I said the best practice
-
is to keep the session default editing context for read only session scope data.
-
Classically, this is the logged in user, their permissions,
-
information like that that applies to the whole session and is not going to change.
-
No objections? OK.
-
You guys aren't much fun today.
-
I save some good ones for later.
-
OK. Now, if you've moved beyond the session default editing context,
-
this is the most common thing to do.
-
Now, you notice the session's just sitting out there all alone.
-
There's nothing attached to it, it's just hanging out.
-
Instead of one editing context, we have a whole bunch.
-
We have one editing context for each page,
-
and each editing context is hooked up to the object store,
-
which is where things go up to the database.
-
Now, because these editing contexts are all equally associated with the page,
-
all equally associated with the data score, they're called peer editing contexts.
-
Now, I'll warn you about a couple things here.
-
If you have an object in one editing context,
-
you can't make a relationship to an object in another editing context.
-
Which is one of the things that Mike fixed in Wonder,
-
with his inverse relation...no. Oh, no. That was in...
-
- [indecipherable]
- Year X generic records.
-
- Yeah. Yeah.
- That's something you have to be careful of.
-
- The other thing is, if...
- [indecipherable] We didn't fix it as much as,
-
we just throw an exception immediately. We fail fast.
-
- Yes, you're right.
- We don't try to...fixing it is nearly impossible.
-
You can't make your relationship between two things.
-
You need to copy objects from one editing context to another.
-
Now, that's a little bit of work,
-
and a few things are, it isolates the changes.
-
You don't have any more dirty sandbox syndrome.
-
Because each editing context is directly associated with one page,
-
the worst thing that's going to happen is that that one page is going to get stuck with
-
the validation errors for the data they typed in.
-
The second thing is data freshness.
-
You create an editing context for each page, so if you want,
-
the data can only be as old as that page is.
-
No effort of yours at all.
-
Create the page normally, it'll get fresh data.
-
You don't have to do any refreshing fetches,
-
you don't have to worry about it.
-
It's because the editing context is attached to the page,
-
not to the session.
-
The editing context gets thrown away when the page gets thrown away,
-
not when the user logs out.
-
Which means that you have fewer editing contexts and fewer objects in editing contexts.
-
Now, a couple of best practices.
-
Because you want the editing context to be discovered with the page,
-
you want a small page cache.
-
The default is something like 30 pages, I think.
-
Which is way more than enough.
-
Because very few users are going to be dedicated enough,
-
rather than going to the menu to go
-
back, back, back, back, back, back, back, 30 times.
-
Probably not going to happen. Five is usually enough.
-
Keep the page cache small...and locking.
-
If you were using peer editing contexts, you must lock.
-
OK? Locking isn't a best practice,
-
locking is a, do it or your app will explode and burn.
-
You have to lock, it's not an option.
OK.
-
The best practice is, don't do it yourself.
-
Don't try and do it in the wake,
-
don't try and do it for sleep, you're going to screw it up.
-
You're going to make a mistake, your app will deadlock.
-
It's been done, it's been tested, it's right. Use it.
-
If you're using Wonder, or if you're inclined to be using Wonder,
-
ERXEC is a fine solution for it.
-
If for some reason you don't want to be using Wonder, you can't use Wonder,
-
there's a class floating around called the Multi‑EC Lock Manager.
-
I'm not fully married to Wonder yet. We're just dating on the side.
-
My class uses Multi‑EC Lock Manager.
-
Anything else?
-
I should have put in more fiery topics.
-
- In the navigation destination of architecture I discussed before,
-
I just flashed a EC on every pop of that stack,
-
and it's resolved the data freshness issue in that system.
-
- Yeah. You can do it, but it's tricky.
-
Oh, I forgot that.
-
I forgot my most important point.
-
Remember before we were talking about having the page,
-
and having an editing context method on the page?
-
If you do that,
-
it's very easy to change the implementation of your editing context method and say,
-
"OK. I'm going to switch from the sessions default editing context.
-
Now I'm going to use a peer one,"
-
and it goes right down through all the components in the page.
-
- Yes?
- May I ask about this...
-
I have...A page comes up, and user does a bunch of stuff on Page A.
-
He gets a lot of records [indecipherable]
-
Then we go to Page B,
-
and they really want all the records from the first pages
-
set up a whole bunch of stuff.
-
Now you do, obviously, get the records from page B without going back to categories.
-
- Yes. Well, it depends. It depends who you ask.
-
- Oh. How do you know that?
-
Do you do some copy from your old?
-
How do you get to the records that are on page A,
-
or page B without doing all this database refreshing
-
and tree walking and all sorts of stuff to create this particularly useful tree?
-
- You can either pass them an array using a method like...
-
- It's component to component, so you have to go through the session
-
to get that because you have no idea whether you got to component B through
-
direction action request or from page A or who knows were.
-
- Are you talking about moving from one page to another page?
-
- When page A goes to page B it can pass the data.
-
- There's no problem putting data into your session....well, there are problems.
-
There's no intrinsic EOF problem with putting data into your sessions
-
when fetched with a non session editing context.
-
You could fetch it on page A.
-
If it truly is session state data,
-
you could push it into your session and leave it in the editing context that you fetched it with.
-
You just have to make sure that you lock it again when you try to touch that data.
-
Or you could local instance into your sessions of auditing context.
-
A lot of it depends on what you want to do.
-
We find that it's very rare that we have session level data
-
that we pass the data between components as part of the action.
-
- Other than the user.
- Other than the user, yeah.
-
Every once in a while there's some crazy problem
-
where you really just have to push in the session because it's just very tricky,
-
but that's by far the exception in my experiences.
-
- Yeah.
- Did that answer your question at all?
-
- Well, I'll look at it. I have a very complex financial application
-
that gets built up as the user is working on it more and more.
-
He may be on page M. He's using lots of stuff he's done on pages A, F, and G on page M.
-
- If that were me I would actually create a new class
-
that encapsulates all the state for this multiple page transaction
-
and pass that object between pages.
-
Because the problem is as soon as you put something into the session it presumes that it is session wide.
-
It's very possible that you go into another page that it's not actually a part of.
-
Now things are confused because it's presuming to get session data and you get a race condition.
-
But this is actually very application...
-
- I was thinking more of what's in the EC rather than sessions.
-
- Are you talking about like a multi‑step process?
- Yeah.
-
- OK, wait a second.
-
The next slide may answer your question.
-
Now this is the alternate step,
-
the alternate organization when you're not using the default editing context.
-
Same as before, sessions is out there all by itself doing its session thing.
-
What we've introduced here is we've introduced another layer of editing context.
-
The pages are tied to an editing context,
-
but that editing context doesn't go directly to the object store.
-
It goes to another editing context
-
and that one there goes to the object store.
-
In order to actually get changes saved doing something like this,
-
you actually have to do two saves,
-
the first save on the child editing context that moves to the parent,
-
the second save gets called in the parent and moves it off to the object store.
-
This can be very useful in some situations.
-
I don't use it very often, maybe a couple of times an application for what I normally do.
-
The big advantage for this, at least in my experience,
-
has been that you get EOF validation without actually sending anything to the database.
-
It's almost like a two phase commit.
-
When you call save changes on the child editing context,
-
it goes through all the EOF validation,
-
calls your validate for save, does all the model validations,
-
does everything it would normally do running out to the database but then it doesn't.
-
It writes it into the parent editing context.
-
I usually only use this for processes that have multiple steps in them
-
where each step is on a page and there are enough things going on on a page that
-
if anything is wrong you want the user to correct it there before they go on.
-
Wizards are a good example.
-
Some best practices for this, again, you have to lock it.
-
Don't do it yourself. Use one of the tools out there.
-
The editing context method on the page should usually return the child editing context.
-
Somewhere on the very last page where the user says, "OK, all of this is right, do it,"
-
you want to have a parent editing context method
-
calling saved changes and that flushes things out into the database.
-
Now one thing I'll mention here is that
-
if you read the documentation you'll see the locking thing is a bit iffy.
-
If you lock the child, you don't have to lock the parent.
-
My advice to you is forget about it, lock the parent.
Don't even think about it, just lock it.
-
You'll never be wrong like that. It's find to lock the parent, just lock everything.
-
Did that answer your question at all?
-
Is that getting any closer to what you're looking for?
-
Everybody happy over here.
-
- On the lock, the documentation is actually right, though? You don't have to go to parent?
-
- No, no, the documentation is right. I didn't say it was wrong. I just said...
-
- He just doesn't trust it.
- Rather than worrying about it, just see an editing context, lock it.
-
- I agree.
- If you can see it, lock it.
-
OK, now this gets back into Mike's neat little trick here.
-
- I'm not really sure if Mike gave this to me or not but...
- Yes.
-
- This is how I started out creating pages in my web app.
-
You know? I'm in page A.
You want to move to page B.
-
Page with name, search page. What's wrong with it?
-
Refactoring, you're using a magic string.
-
Sooner or later you're going to go through there and you're going to realize,
-
"Well, that's actually old search page and I'm going to use new search page for most of my stuff."
-
Then all of a sudden all of your apps are broken but you don't know.
-
You have to go click through every link or do a search and replace and try to find them.
-
Why do that? Make the computer do the work for you.
-
The best practice is when you're doing page with name, do something like this.
-
Searchpage.class.getname.
-
It works exactly the same as that one but
-
when you enter eclipse and you refactor that pages name,
-
it's going to refactor the Java class.
-
It's going to refactor the wool file, the template, the HTML,
-
the DWAD, and it's also going to fix that for you too.
-
If you're silly enough to go into finder and do it in finder,
-
at the very least the next time you compile you're
-
going to get a bunch of compile time errors.
-
Now, with Java 1.5 we can go one better than that.
-
That's a lovely bit of generics code. I love how generic code is almost unreadable.
-
Every time I look at code like that I go, "Ohhhhh."
-
But there's one good thing about generic code.
-
It's really nice to use.
-
You can put that method up in your super class common page component.
-
Now when you create pages now it's shorter.
-
You just have to save page with class, my page dot class. There's no casting.
-
- You probably don't want it to be private in your version, just for the record.
-
- I'm going to put that as a best practice.
- Now who typed that?
-
OK, forget the private part. That should be public.
-
I'm not really too sure why that says private.
-
I copied that from some place.
That's my story.
-
- Well, the funny part is it's like a copy from something I have seen before,
-
but the private you actually had to change, go out of your way to do it.
-
- No, I didn't actually copy it from your stuff.
-
I think I know who to blame, but I won't mention the name.
-
- Yeah, yeah, it was him.
- What's that again?
-
Your X direct action has that method too as well as your X component for the record.
-
- OK, this topic might generate a bit more fire than the other topics.
-
Your components are your view.
-
They're not your controller. They're not your model. Keep them clean.
-
I don't think I've ever seen any web objects application
-
other than a few that I wrote that didn't vigorously, vigorously violate this rule.
-
Some worse than others, but all of them to some degree.
-
Your component is a view. Your component is a view.
-
You shouldn't be calling any methods in EO control ever.
-
The only thing in EO control you should ever have reference to in your page is editing context.
-
If you have any methods or any code in your page that's calling something else in EO control,
-
it's in the wrong place.
Move it.
-
Move it to an EO.
Move it to a controller class.
-
Move it to a business process.
Move it out of your page.
-
Absolutely, positively, under no circumstances
-
will you call anything in EO access on your page.
-
That's really, really bad. Again, if you're doing that,
-
it's in the wrong place.
Move it out.
-
Put it in your EO. Put it someplace else. It doesn't belong on your page.
-
The only thing you should be calling on your page that's not direction on your page are
-
simple methods on your enterprise object.
-
That doesn't mean,
-
OK, enterprise object.
OK, enterprise object give me that.
-
I've got to do a bunch of calculations,
-
and then I'm going to put it back in an enterprise object.
-
That's not the job of the page.
The page is to show things
-
If you have anything other than just simple copying things into the UI,
-
it's in the wrong place.
-
Now so your best practice is if the code is not directly supporting the HTML generation,
-
if the only purpose is not to support the HTML generation, it's in the wrong class.
-
Move it.
-
I might have some fun here.
-
You don't do this, do you?
-
The advantage of this is your pages are really simple.
-
You don't need to unit test your pages because they're really simple.
-
You don't need to debug your pages because they're really simple.
-
There should be very little code on a page.
-
- I don't know about that one.
-
- OK, there should be very little code on a simple page.
-
- I mean the testing.
- I know. I'll convince you one of these days.
-
Any comments?
-
No shrieks? I thought that one would get some shrieks at least.
-
- I think most people agree. It's just whether you live up to the ideal or not.
-
- I do agree. This is really where I go and we all aim to do that. But sometimes we just...
-
- I always do that.
-
- If you look at his code, he does it. He's like the only person in the world to do that.
-
- But I'm not standing on that scaffold.
-
OK. This is a very quick security thing maybe everybody is not aware of.
-
If you make URL like this web objects won't generate it for you, you have to type it yourself.
-
If you type any page name you can go directly to it,
-
even if the user has no links, has no permissions, has nothing,
-
Web Objects will show it to them.
-
Usually it's not a really big problem because most pages need some data, they need some set‑up.
-
What the user is going to get is an exception page probably.
-
But they might not.
-
They may also have to guess the name of your page which maybe isn't so easy.
-
Just in the interest of keeping everything secure and tidy,
-
you can put this rather messy method in your application class.
-
If anybody tries doing something like that, it will just send them off to your main page.
-
You can, of course, replace main with anything else that you want to show?
-
- Actually, no. That one shouldn't,
-
because you don't know what class it is.
-
He's actually right on that one.
-
If this is reusable code that's in your base component,
-
main needs to be dynamically resolved in this case.
-
If you do a ".class reference," it will be guaranteed to be wrong.
-
Because main, you have in each of your apps.
-
I'll give you a shout out that we allow that in the other case.
-
- Thank you.
- Chuck is in the right...
-
However, what I will say is your ex‑component has a slight variation on this.
-
It's actually potentially desirable to be able to do this sometimes.
-
Your X component has a "is page access allowed" method, that defaults to "return true."
-
The intent is that in your classes, you extend and provide a "my page" component.
-
You have your non‑page components and your page component super classes,
-
and your non‑page component classes return false for that,
-
it'll actually throw a security exception, if you attempt to do that.
-
- OK.
- Yeah. That's all.
-
- You can do that, too.
-
Usually, what I do is I return something I look up from a property,
-
but I didn't want to go into a big long explanation about why that was.
-
I just cheated.
-
- I'm curious whether, something we talked about a long ago,
-
anything you can reach to get direct action or
-
anything you do as a result the direct action access to your app?
-
If it generates very much work, it can lead to a denial of service attack.
-
I've never heard of that other than it's a theoretical possibility.
-
I'd be curious if anybody has actually bumped into that kind of thing.
-
- That's a good point.
-
- We should edit that out the DVD, so no one gets ideas.
-
- Just fix it in Wonder.
-
A little piece of advice that I've seen from a few apps is, "Expect problems."
-
The thing you want to remember is bad things happen to good code and that's yours...
-
I hope.
-
Come on quickie...OK. The things you should plan to handle as backtracking.
-
You saw it in Andrew's framework. He handles that.
-
It's easy to forget because you're a developer. You don't use the backtrack.
-
You always use all the controls you get.
-
It's easy to forget that the user is going to delete something and he's going to go,
-
"Oh, yeah, what was that I just deleted?
-
He's going to backup, take a look at it, and bad things will happen.
-
You really need to do something to make sure that you app handles backtracking.
-
As I discovered recently, Safari has a new bug in it and it doesn't work with this.
-
If you backtrack with Safari,
-
your app won't have any idea.
-
I haven't quite figured out what to do about that one, but it's not good.
-
- Safari had that back in the past a couple of years ago and they
-
changed that behavior again at that time.
-
Maybe it was introduced accidentally again and will go away.
-
It was in there, it was removed, and now it's back in?
-
- Yeah. It's clearly wrong. I expect it to be fixed, but for the immediate release it's a problem.
-
The other thing that people often forget to handle are errors and saved changes.
-
Even if you don't have very many validations in your model,
-
sooner or later something is going to happen. Saved changes is going to throw an exception.
-
When you call saved changes, you should always wrap in a try‑catch and do something with it.
-
Don't just assume that it's going to be OK,
-
because your database will go down. Something will happen.
-
Just some general advice,
-
"Bad things happen to good code. Plan ahead."
-
OK. Now we're going to switchover to EOF.
-
Mike stole some of these things in his presentation.
-
OK. The same thing we were talking about with components, use inheritance.
-
EO Generic record is great, but if you start using it for very much
-
you're going to find you have a bunch of common code all over the place.
-
You should provide a subclass for it.
-
Some ideas of things that you will find useful to put in your subclass,
-
a code to copy one EO, make a new EO as a copy of an existing one.
-
Some caching control.
-
It's often useful when you have very expensive values, to be able to cache them in the EO.
-
The problem is that, as data changes, the cache won't be fresh anymore.
-
You can do some things in the generic record subclass
-
to catch certain EOF notification and tell your object,
-
"OK. It's time to get rid of that data and recalculate it." Yes?
-
You can't define the generic clone,
-
but you can come up with a lot of code that makes it very easy to write a copy method.
-
It's in the practical utilities book.
-
I just put a bunch of that stuff in that class.
-
Global ID's. This is something that when you start using peer‑editing context,
-
you start using child. You want the global idea of objects,
-
usually it's "editing context.globalidforobject.EO."
-
It makes it a lot easier just to package it up.
-
Shove it in your super class,
-
and you never have to worry about it again.
-
Related to that is...
-
I think I broke your clicker thing...
-
is an "is new" method, which just calls is temporary object.
-
Rather than having "editingcontext.globalidforobject.EO.istemporary"
-
your code is a lot easier to read if you put in a method like "is new."
-
Validation extensions.
-
I've never liked the API for validate for save.
-
I always found it really painful, because you've got to call super.
-
See if there's exceptions or not, and then add them. Make another and...
-
What I've done is I've made a method that has a different API on my super class.
-
It handles all the paperwork for you. You just have to give it an array of exceptions.
-
Whoops, went too far.
-
Again, define things that you want to put in your own super class.
-
The best thing is to look at what people have done before you.
-
ERX generic record is a good place to steal things from, if you're not using it.
-
There is one in the GVC frameworks.
-
There's several other one's out there.
-
If you don't have something like this, go search through them and find some good ideas for yours.
-
If you do have something, you'll probably find something new.
-
I've ripped off a few things, from Wonder. Thank you.
-
- And likewise.
-
- It's all one big cross pollination.
- There's a new ERX copying coming in. Quite soon actually
-
- Oh, I wonder where that came from?
-
Thank you. I'll give a little diagram here.
-
We were talking before about the generation gap pattern.
-
Up on the top, we EO generic record that comes with Web objects and does everything that you need to do.
-
There's your common EO.
-
That's all the cool stuff you have the Web objects to make your Web objects easier to read,
-
to make it easier to write.
-
Here, we come in with the generation groups.
-
We saw over here, we have group entity, which is in the EO model.
-
That's a definition for the editing and that's used to generate an underscored group Java.
-
The model definition and the underscore class reflect what's in each other.
-
You never touch that. That just gets generated.
-
Down there at the bottom, that's the class you actually use in your app.
-
That's all of your custom code and goes into "group.javaclass."
-
I want to talk about leveraging EO generation.
-
When you were using an EO modeler, there wasn't very much you could do.
-
It just made the accessor methods and mutator methods.
-
There wasn't very much in it.
-
Now that we have all these cool templates, there's a lot of interesting things you can do.
-
Checks for editing context equality, which is what you added in Wonder.
-
If you don't want it there, you can generate it into you classes.
-
It means, next time
-
your app tries to make a connection between two EOs and two different editing contexts...
-
Boom! You find out about it right away, not several operations later.
-
If you're using peer‑editing context,
-
if you're using nesting edited context,
-
you end up copying objects between editing contexts a lot, using local instance.
-
Of course, you have to cast it, because this is Java and everybody loves casting in Java.
-
You can easily add a local instance method into the generated code,
-
to properly cast it to the proper EO.
-
If you're not using Mike's really neat add objects to both sides of the relationship
-
with a really a long name with a Key or something or other.
-
You can generate code to make methods so it always connects both sides of the relationship.
-
Common fetches, I stole this one from Mike recently.
-
- Andrea thinks you should use the class pattern, though.
- Pardon?
-
-Never mind, OK. Andrea's not here, so we're all in agreement.
- OK.
-
It's not many EOs that you have either one of these particular fetches for them.
-
Fetch me all the EOs sorted like this. Fetch me all the EOs with this qualifier.
-
Fetch me all the EOs of this qualifier sorted like this.
-
It's very easy to get methods like that generated for you.
-
Those fetches returns everything nice and casted properly.
-
I haven't actually done this one, but it's been sitting on my desk as something to do for a while.
-
You might want to think about it for entity modeler
-
and to be able to define defaults for the attributes in it,
-
and get that generated into awake for insertion.
-
- Wonder who has something like this.
-
This is part of the thing that Andrea always yells at me about,
-
that I don't use right now. I'll have to look at that and see how it all fits.
-
- Yes, I agree in principle.
- You should probably modify the format to actually accoomodate that directly into system.
-
- Yeah. It would be a good addition.
-
In the meantime, you can stick them in the user info and generate them out.
-
It would be nice to have them official.
-
To get ideas of what you can do, Mike's templates. Mike's a very creative guy in WOLIPS
-
- Those are, by the way, Jonathan Rich's templates.
- Those are John Rich's?
-
- I mean, like the base version. It's 75 percent his and other stuff.
-
- Anyway, I stole a lot of really cool stuff out of those.
-
- We're stealing from lots of them. We're standing on the shoulders like thieves.
-
- That's what Web Objects is all about, cross thievery!
-
OK. Now, again, when we get back to the way people start doing things.
-
The old fetch specifications, your order thing. It's the same problem you had before with the "page for" name.
-
You got a magic string in there.
-
Eventually, you're going to go in and you're going to change the name of the entity or change the name of the attribute.
-
Voila! Your code's broken. You don't know until you run it.
-
You can get the generated files to includes constants like this,
-
where it defines an entity name for order.
-
Then use it like that. When you re‑factor it
-
in the code, it automatically updates it everywhere.
-
You don't have instantly broken code,
-
getting the compiler to work for you.
-
Following on with that qualifier for format.
Bad, bad, bad!
-
It's just a fine source of re‑factoring bugs. That's all it is.
-
It looks really convenient when you start using this,
-
"Whoa, I can do all kinds of things with EO qualifier. With format,
-
I don't have to write all these horribly long qualifiers."
-
Missing a quote?
-
Damm...
-
What idiot typed this?
-
Clearly, I don't use these.
-
That's in version 9 I think.
-
Rather than do that, you should write out the qualifier long hand.
-
Mike mentioned that whoever wrote these classes, really, really liked typing.
-
They like typing more than anything.
-
Even if you're not using much of Wonder,
-
you should at least use this utility class,
-
because it makes writing things so much easier and shorter.
-
- You can use EQ, even.
- EQ?
-
- Instead of equals.
- OK. I like typing a little bit!
-
- We provide both.
-
We let you choose which one you prefer.
-
Anyway, thanks to Mike for making my code much shorter and easier to read.
-
And It's also easy to re‑factor.
-
- It's not actually that somebody likes to type.
-
It's that it's so easy to throw these in a line together at objective C.
-
You get selector, selector, selector, selector going on forever and it behaves perfectly.
-
- Then it translates to java what do you get?
- A mess.
-
Something, something, some...all stuck together with camelcase.
-
- That's a good point.
- It's not that they like to type, it's that you couldn't get any of the...
-
- Objective C, it made a lot more sense.
- Yeah, it did.
-
- That's where we move objects from both sides of the relationship with key.
-
- An Objective C was all broken up.
- In know. I did Objective C. I remember.
-
In Java, it's like,
-
"Add object to both sides of the relationship
-
with gratuitously long name that I have a really hard time remembering what it is key."
-
- It's an old joke at this point.
-
- Yeah. I don't have that many jokes. I have to recycle them.
-
- This is one, actually, where somebody yelled,
-
"Co‑completion." Yes, I totally agree that co‑completion makes this
-
less of a problem on the writing side,
-
but it doesn't make it any easier on the reading side.
-
When you write one qualifier, it basically fills the full width of your editor.
-
You're immediately scrolling to see what the second surprise qualifier is.
-
- OK. I really hope in 2008, I don't actually have to tell people this.
-
That this is the best practice.
-
I've recently found out that I probably do. I'm going to go through this quickly.
-
Use prototypes or you're stupid.
-
- There's a T‑shirt.
- I really can't say it anymore simply than that.
-
Use prototypes or you're stupid!
-
For those two people in the audience not using prototypes,
-
they're templates for attributes.
-
That's basically what they are. It's a template for an attribute.
-
What can you do with the templates?
-
Faster model creation!
-
You're typing less, so you can concentrate more on the business stuff.
-
Less on filling out all the fiddly bits.
-
You can make faster changes, because you've used a template to define everything.
-
If you need to make an update, often you can just go change a template and...
-
Bang! The whole model changes.
-
Rather than trying to search and replace through each place that you used, string 25.
-
Try and find it and change it to string 30.
-
Better consistency, because you don't have a bunch of different people.
-
If someone goes, "Oh Boolean," those are ints.
-
"Oh Boolean," that's a Char 1.
-
"Oh Boolean," that's a Varchar 5.
-
You're just picking the Boolean templates.
-
You have much better consistency across your model, consistency in string lengths.
-
A big one for me, this is the reason I got into it, database independence.
-
You define different prototypes for different databases.
-
It's very easy to switch between them.
-
On the project I'm working on now, we've got a CIO who says,
-
"I really want you to stick to the corporate standard of Microsoft SQL server,"
-
which really sucks, by the way!
-
We want to use FrontBase, because it doesn't suck!
-
In order to keep them happy, every night we have this whole bank of tests.
-
We run it against Frontbase. I flip the prototypes,
-
and then we run it against it against SQL server.
-
That's with one little configuration change and there you go! Use the prototypes.
-
- And also, any Entity Modeler is specifically designed for use with prototypes.
-
If you find Entity Modeler is annoying to use, it's because you're not using prototypes.
-
There may be other reason, too. Don't get me wrong. But that one in particular.
-
- Yes.
Pardon?
-
- Like the DVDs?
-
- Do you mean, how to do it?
-
- It's in Wonder, of course.
-
- ER prototypes in Wonder.
- Yeah. ER prototypes, there's a description of it in practical Web Objects.
-
If I recall correctly, there's stuff in the Wiki as well.
-
- It's also in the old EO model of documentation PDFs.
-
Actually, using EO prototype has been part of object since the beginning.
-
It has always been the best practice. It's never changed in 12 years.
-
- A long time ago, not too many people did this.
People are slowly learning.
-
- Just to be fair, prototypes were mostly brought in as the latest fashion of EO,
-
just before it was replaced by the awesome tool that Mike wrote.
-
That's why most of the people didn't use them, but they were part of the WO specification from the beginning.
-
- Again, use EO prototypes. If you are, well you know what's wrong.
-
- We actually reengineered them for 5.2. They're now extremely reliable.
-
They've got some really nice cool stuff that you can do with prototype,
-
so really I encourage you to go and have a look.
-
- You heard it from Pierre.
- Except for the bug report I just filed.
-
Yeah. That's a good point.
-
There really is not a single place for this that has all the layers.
-
- You're book talks about it, but not...
- It's outdated, yeah.
-
- The concepts are definitely...
-
- The concepts are the same, but the screenshots would be wrong
-
- ...the screenshots would be wrong, basically.
-
- One of these days, maybe.
-
Let me know when you finish it.
-
That's probably a good point. Something, somebody should write something about it.
-
If anybody reminds me, maybe I'll do it. Otherwise, I'll forget it.
-
- Yeah. We're not done yet.
- He's got one.
-
- He's got a book on the shelf, like waiting to go!
-
- I might add something to the WebObjects Wiki on Object style.
-
I can copy a chapter of our class book.
-
Then we have some central one‑step place to go to read about the prototypes.
-
- That'll be great! Thanks!
-
- Jeter wrote the class book on the big nerd ranche's Web Objects training sessions, just for background.
-
- Gave a course along with it, now.
-
What month is it now?
June.
-
Moving on, on our EOF thing, monitor the SQL.
-
The great thing about web objects is you don't really have to think about the SQL.
-
You make your model, you make your Java,
SQL takes care of itself.
-
But forgetting about the SQL entirely is at your own peril
-
because as you showed this morning, if you are not watching but the apps dumping out,
-
you can run into some pretty serious problems.
-
In order to see the SQL your application is generating,
-
you just launch it with that simple parameter,
-
and in your log you'll see all kinds of SQL.
-
I'd advise you at least some of the time when you are working,
-
have this on and actually look at what it's generating.
-
If you have it on, you don't actually look, it doesn't really do all that much good.
-
It slows your app down a little bit.
-
When you look into this SQL, what are you looking for?
-
One of the things you're looking for is repeated single row selects.
-
Select from customer. Select from customer.
-
Select from customer. Select from customer.
Select from customer.
-
Because if you're seeing it doing that,
-
then you're probably needing either batch faulting in your model,
-
which you can see over here on the right down at the bottom.
-
Set the batch faulting in size in the advanced tab.
-
The default is one, which again,
-
I said before, some of the defaults and WebObjects, one isn't really very useful.
-
Even if it's two, you'll get half the number of fetches.
-
Probably for most apps, for most relationships,
-
you want something a little bit bigger than one.
-
When that doesn't work, or that's not doing it for you,
-
there's this really cool class in Wonder,
-
that you really want along with your XQ, called ERX recursive batch fetching.
-
Worst name ever.
-
- But a great class.
- But a great class.
-
- I've fallen in love with this over the last year or so.
-
It's really good for optimization of fetches. I've run into a few odd problems,
-
mostly with SQL Server,
-
and pre‑fetching, and some weird inheritance things
-
where it doesn't work, that class does.
-
That's another reason to look at it.
-
I got to talk to you about that later.
-
The other thing you're looking for besides single row selects are slow queries.
-
Something that's taking too long.
-
Too long being a relative term for how much data you have in the database,
-
how much data you expect in the database.
-
If you've got slow queries, one of the things you can use is,
-
again from Wonder, it's full of all these neat little tools.
-
Again, you don't need to marry it. You can just see it on the side.
-
ERX adapter channel delegate, you can turn that on and it'll log out queries.
-
It will log out, you can say, "OK, any query that takes longer than 200 milliseconds,
-
I want to see that. Any query that takes longer than a second, I want to see that."
-
You can do some other fine tuning. I forget, like,
-
"I only want to see queries in this entity name."
-
But it's a good way to find out what in your app's taking longer than you really want it to.
-
The usual answer to that is your missing indexes.
-
Usually you can make the query fast enough by adding indexes.
-
A few times, I've found that you can't, just whatever the SQL is, the database just doesn't optimize it correctly.
-
The thing to try then is some of Pierre Bernard's qualifiers from the HOW‑TO frame works,
-
or some of the qualifiers from Wonder,
-
some crossover between them and often by qualifying it differently,
-
you can get it to generate different SQL.
-
I've seen it go, like, from 40 seconds to a quarter of a second,
-
just by using a different qualifier, same result.
-
- OK. Index, it's really, really useful.
-
Nowadays you can actually define them in your model.
So...
-
Use it.
-
- As a 5.4.
- In 5.4 Yeah, and in [indecipherable] , too.
-
It's supported, it's you can do it directly from the UI.
-
You just define it, it's going to generate a proper SQL and so it's really easy to use.
-
Just put the index in the model.
-
- Yeah, when 5.42 comes out which should be really soon now...
-
- We hope.
-
- This is going to be a great feature. I'm really looking forward to that one.
-
- It's actually been in 5.4 since zero,
-
but because Entity Modeler uses the 5.3 APIs it didn't work quite right for me until 5.4.
-
- Can I just add to that for a sec?
-
Put on a database hat for a minute.
-
As somebody pointed out in the audience,
-
it does make sometimes not so much sense to add too many indexes.
-
Testing, testing with large data sets.
-
Testing your queries and seeing how they run, finding,
-
if you don't have the experience yourself to look at the database, get someone who does.
-
I had a very interesting production environment.
-
The database house was owned totally separate from the app side.
-
The database side was like,
-
"Oh, yeah, you run that query. You asked us to profile it. It runs in three seconds."
-
The app guys are saying it's the database guys,
-
and the database guys are saying it's the app guys.
-
They're having this great fight.
-
I managed to have a look at it. Yeah, it's true.
-
That single query runs in three seconds, no problem.
-
Except that the app guys don't realize that a single user instance of a click,
-
someone in a browser, initiates 3000 of those fetches.
-
The app has this little bit of trouble.
-
Yeah, profile. Check out what's going on.
-
Watch your logs, and test it and see what happens.
-
- Thanks.
-
- If you're using Oracle, watch out for everything.
-
-I was going to say, stay away from Oracle.
-
- I disagree. I grew up on Oracle
-
from Oracle four on, and there are some really awesome features in Oracle.
-
- That's so sad.
-
- It would take those 3000 queries and actually work whereas mostly [indecipherable]
-
- Oh yeah, no, no, no, no.
-
The 3000 queries was, in fact, Oracle, and it did, in fact, work just fine eventually.
-
- 9000 seconds later.
- It just took a while to run, no problem.
-
- Are you saying qualifying the dates or indexing the dates or both out of curiosity?
-
All of the above?
-
Oracle dates, time stamps.
-
Sequel, JBC when you're using a NS timestamp will output a Java sequel timestamp.
-
If you have your Oracle column set to date,
-
Oracle then in the database has to convert that Java sequel time stamp into a date.
-
If you've used that date, say as a partition column,
-
you're going to have a query that takes 100 to 200 times longer to execute than
-
if you had just passed in a time stamp, or if you used a time stamp for your partition column.
-
There's a nice work‑around.
-
I'm not sure if there's a techno publish for it.
-
We discovered it last year at WOWODC.
-
It's basically telling Oracle to work in the 8I mode.
-
You lose all your 10G features.
-
OK, next one I'll go through pretty quickly. Awake from insertion,
-
it's the EO constructor.
-
If any of you have ever tried to use the actual Java constructer in your enterprise objects
-
you probably quickly discovered that wasn't really a good thing to do.
-
If you want to do your initialization in awakeFromInsertion,
-
it's the place to set defaults for your enterprise objects.
-
The little known fact that I want to point out today is that it can get called more than once.
-
Two cases have been pointed out recently, Java client
-
and nested editing context can result in this getting called more than once.
-
In order to protect yourself
-
the best practice is to check to make sure a value is null before you set the default.
-
If not, then probably something else has been done with that object
-
and you don't want to go back to the default.
-
- There is also the init method you can override if you're using Wonder
-
which checks whether it's a temporary global ID or not.
-
If you deleted an object from an editing context
-
and do an undo on the editing context it gets reinserted.
-
- Oh, that's another good point, yeah.
- You want to set up the default values there.
-
Use in it or check whether the global id is a temporary global ID or a non temporary.
-
- OK, now a few quick Java ones, exception handling.
-
Don't do this.
-
How many times have I seen code...
-
I thought somebody would like that.
-
Don't write code like that.
Just don't do it.
-
I've seen so many people do that and it's just,
-
"Oh, it will never happen. Don't worry about it."
-
Sooner or later, yeah, it does happen.
-
- What if you expect it and it won't hurt?
-
- Whatever you want to do in the privacy of your own application is fine with me.
-
- He's right. That does occasionally happen.
-
For me when that happens I always put a comment in that catch,
-
and explain why I'm doing that.
-
Because someone is going to come back behind you and wonder...
-
they'll think you're a bad programmer.
-
- Yeah. On the very rare occasions when you expect it
-
and it won't hurt and you're dealing with it some other way,
-
then, yeah, just comment it. But don't leave an empty block like that.
-
The problem is, though, if you don't have that in there then you have to say,
-
"OK, this method throws parse exception."
-
Then all the methods that call that have to throw parse exception.
-
You end up making changes all over your code.
-
It's a real irritation of Java checked exceptions.
-
The best practice for this, instead of swallowing the exception,
-
convert it to unchecked and re throw it.
-
- Or throw it. Or throw the original.
-
- Yeah, but if it's checked then you have to declare it all the time. - Yes.
-
- As opposed to...
-
nobody likes to do that. Nobody is going to do that.
-
Nobody is going to make that many changes in their code.
-
This forward exception has a little bit of a private API call that you can use there
-
if you want to stick with that. Pierre is shaking his head going no,
-
and I agree with Pierre because I don't do that.
-
- I hate that feature of Web Object.
-
It's forward exception is something that...
-
objective C polluting Java, it's...
-
- I get this from Wonder, and I blame Wonder for making me do that.
-
I did it the nice way and Anyo got cranky at me.
-
There's actually a class called Exception Converter.
-
In the notes to the slide there's a URL for it, and it's a much nicer thing to do.
-
- Well, you run that exception?
-
- Yes, but the problem of throwing in a run time exception is you lose the original stack trace.
-
- You chain it, you chain it.
- Not if you put parentheses E.
-
- Yes. Actually parentheses E plus exclusion as to what in this level why it's being thrown.
-
- Chain, chain, chain always chain.
- No.
-
- I would say definitely and the most important is to throw.
-
Beyond that your personal religious beliefs on
-
throwing checked or not or changing or not, are less important,
-
but not swallowing it the really important thing.
-
- OK, at least we can agree on that much.
-
The rest of it is just a quite good practice.
-
Use formatters.
-
A lot of people seem to be afraid of these.
-
I ended up using them all the time.
-
They're easy to write.
-
If you look at the whole API for, was is Java text format,
-
there's a whole lot of stuff there. But,
-
with Web Objects, you usually don't care.
-
You're formatting one value of an input,
-
and you're...you're parsing one value of an input or you're formatting it into a string.
-
You don't need to get too carried with writing fancy, fancy formatters.
-
It's good because it keeps the views separate from the model and the controller.
-
Rather than using a formatter, you can often just do it in a WO component.
-
The thing is that this is a little bit smaller than a WO component.
-
It's easier to reuse across a bunch of WO components.
-
It promotes reuse because of that.
-
The code's not tied to one specific page.
Yes?
-
- This is going to get ugly
-
A lot of the formatters aren't thread‑safe.
-
I think all the ones I rate are thread‑safe.
-
But, I know some of the Java timestamp formatters, and some of the other ones aren't thread safe.
-
The best practice is probably to cache them in a session
-
if you're not going to get multiple requests,
-
unless of course, you're using a long response page
-
in which case then you've got a different problem.
-
The best practice is, it depends but think about thread safety.
-
- Yes. None of the Java formats are as thread‑safe.
-
- Sorry. It's actually not that costly to pick a formatter.
-
Just don't try to share a formatter between threads. You're asking for trouble.
-
There's no doubt, I put the date and time into the session
-
as seen from the slides to hold the time zone in them?
-
But the rest of them I just make on the fly because they usually don't have any data in them.
-
- Also Wonder Helper functions.
- Wonder Helper functions?
-
- Nice.
-
Just a couple of quick examples on how to write very simple formatters.
-
This one here just takes an NSArray and
-
puts it with a bunch of page breaks between it.
-
You can easily deal with a little WORepetition and a string,
-
but then you've got another component with a WORepetition and String
-
or you've got part of your component that is less reusable than it could be because it's inside of it.
-
That "return buffer.append" in the format command
-
is the entire implementation of the formatter,
-
and it's not particularly difficult.
-
We're not going to bother parsing this so I just throw an exception.
-
But, those are the only two methods you actually have to implement to make a formatter.
-
If you're doing output only, it can be as simple as one line long.
-
A little bit more complex example.
-
This one here converts between separators in a string.
-
I'm working on a system now as a bunch of legacy stuff,
-
and some things are comma separated and some things are tab separated.
-
I use a formatter to move things back and forth.
-
You can also do the same thing
-
if you've got a string of text that somebody's typed in a text box
-
and you want to format it out in HTML to keep the page breaks,
-
it'll do that.
-
If you've got a bunch of HTML like from an XML file
-
or something and you want to convert it back into a paragraph, it'll do that.
-
Again, there's really only two lines in it that do anything.
-
The format one just does, just calls a replace along the string from the internal to the external.
-
The parsing just changes it backwards.
-
That's all you need to do.
-
The only thing special, if you notice the first line in the parsed object,
-
it was position set indexed string length.
-
You just have to set it to something.
-
If you leave it at zero, Java will throw an exception for you.
-
Just set it at something, keep Java happy.
-
There you go. Formats and parses, there's only really two lines of code.
-
If you start using them, you'll find all kinds of places you can use them.
-
I like them a lot.
No comments?
-
- Position set index can be or even important if you're chaining format is to give one
-
inside the other because you don't know where you're
-
starting and ending in the paths for the next one.
-
- Yes, but usually I try to keep them fairly simple.
-
Yes, if you're trying to do like a full blown formatter,
-
then you actually do need to worry about the position.
-
- The point of the Wonder Helpers, by the way, is
-
that formatters you can only...well, from the binding, from binding,
-
you can only use them on components that expose a formatter finding.
-
Wonder Helpers you can use on any binding.
-
- That's nice.
- I don't think I've used those.
-
- You should because you steal them.
- I will.
-
- Right, right. You can have multiple...
-
- They can be extended into something else.
-
- I usually just chain the formatter instances.
-
- No instances.
- We won't go there.
-
OK, so, moving on to our final topic, I think we're, are we out of time Pascal, or Keep going.?
-
Deployment, trying to get through here quickly.
-
There's really not much to say about deployment that hasn't been said in other places.
-
Manage dependencies, plan for change. It's going to happen.
-
Web Object extensions directory sounded like a great idea when it first came out,
-
jammed all kinds of jars in there. It was wonderful.
-
Then I needed to update one app in the server and I couldn't update the other apps and it was terrible.
-
For deployment, my advice is just delete everything in that directory.
-
It's just a dependency nightmare waiting to happen.
-
Instead, if you need it, if you need jar files,
-
put them in a framework, use the framework in your application.
-
Framework goes into your subversion repository wherever your repository is.
-
Manage the dependencies like that.
-
In fact, I'll go one further and say,
-
"You should embed all frameworks."
-
All frameworks, I include the Web Objects ones as well
-
because that way you can deploy applications using different versions of Web Objects,
-
different versions of the jars on the same server.
-
You don't have to worry about conflicts.
-
And Yeah, people are going to say,
-
"Oh, it's going to be bigger. Oh, no."
-
I'll happily trade off some FDP upload time for manage dependencies.
-
- OK. Two comments on that.
-
Most embedding framework,
-
I would encourage you to embed a jar version of the frameworks.
-
If you look at since 5.2, I think.
-
You can actually jar your framework,
-
and they would work just the same.
-
You can just dump them in your application in content extensions,
-
and they're going to get loaded exactly like any other framework.
-
It's very compact, it works really well.
-
You cannot [indecipherable] jar.
-
You can have flat jars which are the same.
-
The other comment that I would say on the size of the app,
-
is that gziped your app, before you upload them, you will save more time.
-
It's incredible, I have seen so many people trying to upload a .woa not zipped.
-
It takes forever because the protocol are usually very inefficient on starting and stopping files.
-
You are going to upload hundreds of small, tiny files, which is crazy.
-
- Yes
-
- gzip's them, you'll be surprised you can save one, if not two order of magnitude in the upload.
-
- If I can continue with that, once you have zipped it and put it up once,
-
use RSync to sync it after that, because then you only move differences.
-
- Don't do that.
-
The reason you should not do that is because you will have a running app on the old version.
-
If you have multiple instances,
-
you really should upload it into a different folder and do a version swap so you have...
-
- To be clear, you should shut down the production site prior...
-
- No, no. You don't want to shut down the production site.
-
You want to keep it running.
-
- You just Rsynching the zip file, not the app.
-
- Then you have to unzip it on the server.
- No, you're proposing unzipping the WOA, I think.
-
- I was Rsynching the WOA, right? But I shut down the site.
-
- If you don't want to shut down your production site
-
and you don't want to be replacing your WOA in line.
- Yes.
-
- Especially because Java catches jar index offsets.
-
- If you replace a jar in a running app....
- Things get very, very interesting.
-
- ..you get horrible, horrible problems.
-
- If you try to load a component that hasn't been loaded yet, the jar offset is wrong
-
so it will just give you a bizarre "no class.found" error.
-
It's a bad error. It's bad. That is all.
-
- Because I noticed it the other day and because I think it's great,
-
when you look at 5.4, they now ship examples in a format where you build ANT
-
and you get legacy, JAR, and WAR versions fully embedded of all the examples from Web Objects
-
If you don't know how these work or you don't know how to build them,
-
they ship with 5.4 examples now. Thanks, Pierre and whoever else made that happen.
-
- It was actually Daryl.
-
- OK. Thanks Daryl.
- Thanks Daryl.
-
- Is that a hand in the air?
-
- He may have been the shadow we run to the door.
-
- I just have a quick question for Pierre.
-
When you compile those jars into the extension directory,
-
[indecipherable] and you still have to put them back in the [indecipherable], correct?
-
- No.
-
- If you just shovel them in there then you're lost when the application starts?
-
- Any jars in your slash content extension
-
are going to be loaded before anything else in your class paths.
-
There is nothing you have to do.
-
It's even loaded before the library extension.
-
If you want to override a JAR, you just put it there.
-
It's going to be loaded before anything else.
-
- OK. I just want to go through one quick final topic here,
-
and we're running low on time. Don't repeat yourself really.
-
I've only got so many jokes. I have to use the lame ones.
-
One of the things that bothered me when I first started using WOlips
-
is I had all these build files for each project,
-
and they were all substantially the same.
-
I always had to go in there and fiddle them.
-
Then another version of WOlips would come out, and I'd have to change the things again.
-
In ANT 1.6 they added the ability to import one build.xml file into another.
-
That's a really good thing to do.
-
For an example of doing things like that, see the Wonder build system.
-
It is really quite complex, but you can find a lot of interesting things in there.
-
I'm currently redoing our build system
-
so that I have a couple of shared files that all the projects use.
-
This is what I've ended up for a build file for a WO framework.
-
That's it, three lines and a couple properties. It's a lot easier to maintain.
-
- OK. Just one comment on that.
-
Look at Maven. You can actually build some awesome tools with Maven.
-
I think there was a demonstration last year from Jake.
-
Maven is actually an extremely good tool to build Web Objects.
-
That's how we build Web Object every night.
-
We use Maven. We've got a Maven build system for Web Objects.
-
It's really awesome. It takes care of all the dependency.
-
It's really reliable, in fact, you don't have to write much code if you accept two of the Maven conventions.
-
- I don't.
-
- We use some funky tools. You can ask Mike.
-
Every time I tell him one of them he goes, "Oh, I want to die."
-
Our build files are highly customized,
-
so I don't know how happy it would be with Maven,
-
but I'm not going to go there.
-
There was a question and answer here, I've been doing some questions. I'm going to skip over this.
-
We don't have time to go into the fun topics,
-
but if you want something to talk about over beer tonight...
-
- They're hard to read from this distance, and we'll take that as a good thing.
-
- It didn't look so bad on the screen. I was trying to find something
-
that didn't look quite so horrible. Just a second here.
-
Here, we'll just do this.
-
- Not much better.
-
- OK, OK. Everybody is a fricking art critic.
-
- If you wanted ugly discussions, this is the slide for you.
- Hold that. I can't do this.
-
Oh, oh no.
-
This is like way more than my French can handle.
-
You can deal with the blue. I'm sorry.
-
I can read cereal boxes and soup cans but....
-
I decided it will be a fiery topic.
-
It makes spaghetti bindings.
-
- I use it a lot, but it still can make spaghetti bindings.
- We validate that, by the way, now.
-
- [indecipherable] "bindings are spaghetti [indecipherable] "?
- We validate.
-
Even the bindings part, oddball expression.
-
At least, it might be spaghetti, but we'll tell you if you have a bogus binding in the middle.
-
Personally, I like WOOgnl, we use it all the time.
-
But I've talked to some people who thought it was harmful.
-
- I feel bad every time I use it. But yeah, we use it, definitely.
-
- Just wash your hands afterwards, and everything will be fine.
-
Right? I think that's it. Thank you, everybody.