Archive for the ‘WebObjects’ Category

Welcome to the Leopard WebObjects mess

Tuesday, October 30th, 2007

So, you thought Leopard and WebObjects 5.4 would bring some good news to WebObjects developers, right? Well… so did I.

I’m not even going to talk much about Leopard itself. It’s clearly an unfinished product, with dubious interface “improvements”, and totally unstable, at least on PowerPC macs. I try to keep my blog tecnhically-centered, and not based on personal opinions, but please Apple… you can do much better than that. Stop playing arround with the iPhone and concentrate on what really matters.

Well, about WO… Leopard has a new version of WebObjects, 5.4. It has some bug fixes and improvements over the previous versions, but unfortunately there are still a lot of show-stoppers that keep me from installing Leopard (and as you can guess from the last paragraph, I’m not specially motivated for that).

The first one is that Wonder still does not compile on 5.4. So, if you use Wonder (and you should), you must bring WebObjects 5.3 to Leopard. It’s not a very complicated process, but of course, it means trashing the brand new version. The good news is, if you use Wonder, you don’t really need WO 5.4, because almost all the features are already implemented on Wonder. The ideia is installing 5.4 (basically to get the Apache 2.2 adaptor), them trashing everything else, and finally installing 5.3. I installed it from the xCode 2 DMG and it worked fine. Anyway, Milke did an excellent (as always) document about his trip back to 5.3 on Leopard.

You should note the detail that the plists for LaunchD to boot WOTaskD and WOMonitor are not even in Leopard Client. Apparently, someone actually forgot to include that in the release. Assuming that most people develop without direct connect enabled (I don’t know how true this assumption is, but at least I do!), implying that they actually need WOTaskD to be running, it’s a bit scarry to see that actually no-one at apple tested this, or else they would find out that the files were not included. It surely doesn’t boost my confidance on all this.

Also, I develop using virtual hosts. Basically I set the adaptor URL, in WOLips launch options, to be something like http://aVirtualHost.mac/cgi-bin/WebObjects. I kept getting the “No instance available” message until I run “sudo hostname -s aVirtualHost.mac” on the command line and restarted every WO-related process (apache, WOTaskD, and the app itself). BTW, note that the file included in apache2 configuration for vhost configuration has errors, and will actually kill apache at launch if you don’t remove both examples that come with it (the error is in the logging configuration for both sample virtual hosts). Even worse, if you are trying to look for apache error logs in the error_log apache file as you always did on tiger… forget it. This kind of errors are now thrown to /var/log/system.log.

But the biggest show-stopper is EOGenerator. You do use EOgenerator, don’t you? EOGenerator was based on the same frameworks as the Apple WO tools, like EOModeler. Apple moved those frameworks from “deprecated” to “dead” with Leopard, so EOGenerator went to the grave with them. Relax, there will be a Java replacement included on WOLips, but it’s still not there. Well, talk about living in the information age… the EOGenerator is out! :)

So, if you are an WebObjects developer wondering about jumping to Leopard… my adviced is, wait. Unless you don’t use Wonder and EOGenerator, which is highly unlikely.

Recovering from optimistic locking exceptions

Friday, August 17th, 2007

WARNING: the solution presented in this article is wrong. It’s a partial solution only, it won’t always work, and you may screw up your data. To see why, check my second article on this issue.

There are many situations, in simple web applications, where you don’t really have to deal with concurrent data update. Imagine you are writing a blog app, like WordPress. Even if two persons are changing a post title at the same time (which, by it’s nature, it’s a really rare event), you, as the developer, may simply don’t care about it, and rely on the “last write wins” strategy.

When you are writing more complex stuff, specially when one needs to have really complex data being modified by different users, in different situations, at the same time, and when you have pre-calculated data, because grabbing all the objects and calculating the results in real time (fast enough for a page load) is simply not possible, you will have serious headaches (it’s already hard enough to understand this sentence without re-reading it!).

When developing WebObjects, there are two main classes of concurrency problems to solve. Intra-instance concurrent data updates, and inter-instance concurrent updates. It would appear that the second is much harder than the first… well… it’s not.

Intra-instance updates should be simplified by automatic data merging between contexts. But there are some problems. The biggest problem is that, in fact, it’s possible to totally overwrite a data modification done by another user without getting any notification of that at all. How? See this thread on the WebObjects Dev mailing list. The biggest problem is that, while a context is locked, data merging simply does not occur. So, imagine you lock context A and start modifying data. At the same time, someone locks context B on another thread, changes the same data that you are modifying in context A, and saves it. The data you changed is merged on all the other contexts where the same objects are present, as long as they are unlocked. But remember, your context A isn’t, so no merging will occur for now. Now, imagine you continue working on context A, and finally save it, and unlock it. The merging will occur now - to late, because you already saved, and probably won’t work any more in that context.

By now, you say “Naaah, that won’t happen, because WebObjects will trigger an optimistic locking exception”. No it won’t. Why? Remember that context B saved while context A was locked, right? So, when B saves, the row snapshots of the EOF stack will be updated. When you save context A, assuming that no one else (besided the A and B guys) worked on the same data at the same time, EOF will base the OL check on the row snapshots that is has in the stack. Remember, this are no more the snapshots for objects in context A. They are the snapshots caused by B saving, and those will be the same that exist in the DB. So, no OL exception will occur. Congratulations, A just trashed B data updates, and wasn’t notified at all.

Are you already scared? Good. So be even more scared. There are only two solutions for this. The first one, create a lot of separate EOF stacks (one per session, assuming the sessions are being locked in the normal WO way). This sucks, because it’s too resource expensive. It will use a lot of memory (lots or repeated snapshots in memory) and it will open many connections to the DB, that may be a problem by itself. The second solution… classic Java locking (syncronized keyword, etc). This is the time you start thinking about being a farmer, right?

Well, the news on inter-instance concurrency are better, although far from perfect. Here, there’s no data merging, and no Java syncronized stuff. Everything will be based in locking. Usually optimistic locking, but you can also use pessimistic locking (ie, “real” row-database locking). Pessimistic locking has huge problems and many experienced coders will recommend that you stay away from it. So I will not cover that, and I’ll assume you want to use Optimistic locking.

The ideia is simple: create context, fetch data, modify data, save. If you get a locking exception, re-fetch data, re-modify, and re-try to save it until no OL exception is thrown.

This is simple for simple stuff, ie, when you have one single object causing the failure. Now imagine the following scenario (and this isn’t imaginary, it occured to me): you are working with dozens of objects thay may cause OL exceptions, and if one of them causes it, all the others will probably cause too. Also, I need to create some objects (relationships, mostly, but also some individual objects) depending on what I’m doing. If I get an OL exception, I must go back, delete those objects, and create new ones, because the fact that I create, or not, an specific object depends on the data already present on the data storage. In theory, this will not be a problem: create context, fetch data, modify data, create objects (and save the created objects in a temporary array), save, get OL exception, delete all the created objects, re-fetch data, re-modify data, re-create objects, save. Right? Wrong. Unfortunately I’ve hit some obscure WebObejcts bug, as you can see in this WODev thread. This is the time you start browsing the net looking for a farm to buy, if you don’t already have one.

After a lot of experiencies, I think I have reached a reasonable way to handle this in a simple manner (remember, you will always have to think about intra-instance problems, but we are dealing with inter-instance now). Instead of fighting with WO about deleting created objects, and getting outdated data when one was supposed to get fresh one, simply don’t worry about it, and create everything from scratch every time you try to save. The code will look something like this:

synchronized (lock) { // Solving intra-instance problems,
                      //your millage may vary on this, of course
  int tries = 0;

  // Make MAX_SAVE_ANSWERS_TRY_COUNT something reasonable, like 50
  while( tries < MAX_SAVE_ANSWERS_TRY_COUNT ) { 

    // Setup context
    EOEditingContext context = new EOEditingContext();
    context.setFetchTimestamp(System.currentTimeMillis());

    // Make sure nothing "strange" will happen, this is a simply delegate
    // that blocks merging. Probably it won't be needed but I'm paranoid.
    context.setDelegate(new NoMergingECDeletage());

    // Register the EC in my session lock manager, to handle locking and
    // garbage collection automatically. Your millage may vary, specially
    // if using Wonder in a smarter way than I do.
    session.lockManager().registerEditingContext(context); 

    // Get local copies of objects
    < here you get all the local copies of your objects. Remember the
    context.setFetchTimestamp(System.currentTimeMillis()); line above?
    This will guarantee that ALL the objects you get in this context will
    contain fresh data. You can use EOUtilities.localInstanceOfObject,
    walking trough relationships like object.otherObject().anotherOne(),
    using fetch specifications, whatever. Everything will be fresh. >

    // Let’s do it.
    < Do your thing here. Create objects, modify objects, delete objects,
    go crazy. >

    // Try to save
    try {
      context.saveChanges();
      return;
    } catch (EOGeneralAdaptorException saveException) {
      ++tries;

      // isOptimisticLockingFailure is basically the method
      // with the same name in Apple docs
      if( Util.isOptimisticLockingFailure(saveException) ) {

        NSLog.out.appendln(”Optimistic locking exception”);
        // Note that I don’t refault anything here. I don’t need it.
        // On the next iteration, a new EC will be created, and all
        // the objects will be automatically refaulted.
      } else {
        // It’s some other exception, handle it somewhere else
        throw saveException;
      }
    }
  }

  throw new RunTimeException(”Could not save after ” +
MAX_SAVE_ANSWERS_TRY_COUNT + ” tries. Help me.”);
}

At the beggining, I didn’t like this approach at all. But after using in some places, and seeing it work perfectly, I’m liking it more and more. The main advantage is, it works. No strange problems, no fighting with WO bugs, no strange refaulting behaviour. It’s a dream come true. It just works. But, of course, it has some disadvantages. All the objects are fetched from the data store, with the consequent performance hit. So, if you are dealing with many many objects, this may be undoable for you. Also, it has a more serious problem if you are lazy like me: you cannot have an object binded to the page component, and use it in the processing. The reason is that you need to bring that object “inside” the newly created EC, and localInstanceOfObject won’t copy the unsaved modifications, and will not even copy newly created objects. So you have to manually copy and reproduce all the user modifications to the local copies of objects. More work for you, more work for the CPU, more objects in memory. It’s life.

Written at the beautiful and peaceful town of Serpa. Not on an iPhone.

WebObjects survey

Friday, June 29th, 2007

Pascal Robert has just setup an online survey for WebObjects developers. The survey is short and direct2thePoint :) so you won’t spend more than 5 minutes on it. The results will be delivered to Apple, to help them focus their development resources on the needs of the community. As Pascal said, the rules are:

  • - One survey per organization, if I see the same organization more than one time, I will delete the duplicates
  • - Please, please, be honest in your answers

So, if you are an WebObjects developer, take the survey now!

svgobjects 2.0

Monday, June 25th, 2007

As you may know, Safari 3.0 finally fully supports SVGs. Based on that, Ravi Mendis released the first beta of svgobjects 2.0, an SVG framework for WebObjects. Apparently, the framework started with the goal of drawing charts dynamically, but the author extended it to support the drawing of full-screen interfaces that could be used on the desktop and, even more, on mobile devices, like the iPhone. It may become a very useful framework for building iPhone-targeted web apps!

WebObjects rocks

Friday, June 15th, 2007

Well, WWDC is almost done, I’m waiting for the last session on Level 3 of Moscone, near one of these really big windows.

It’s a really big pain to not being able to write here all that I have learned about WebObjects (for those of you who don’t know, WWDC sessions are covered by NDAs).

Someone asked on the WebObjects mailing list how do we classify the WO news, from A to C. Well, I can say, A++++! I think the future of WebObjects will be susprising. Apple showed us some stuff that, in my opinion, is probably almost as revolutionary as the original WebObjects framework itself. But we still will have to wait.

The only thing I can say more is: if you are (considering) doing web application development, don’t be stupid - learn WebObjects NOW. Currently, it’s the most powerful web application development environment by far. And in some future time this advantage will be an order of magnitude higher.

W(O)W(O)DC 2007

Wednesday, June 6th, 2007

I’ll go tomorrow to SF, to assist WOWODC 2007 and WWDC 2007, so I won’t post here for some time.

WOWODC was organized by Pascal Robert, as a result of the lack of support Apple gives to WebObjects developers, and will feature some well-known people in the WebObjects community, like Chuck Hill, Mike Schrag or Anjo Krank, who we will certainly flood with questions. Mr. Pierre Frisch, the WebObjects Product Manager at Apple will be there too. This will be a great year for WebObjects, as it’s community will provide the attention it deserves! :)

See you in two weeks.

Emulating a slow internet link for http

Tuesday, May 22nd, 2007

When developing a Web Application, many times you need to test how will it react then operating over a slow internet connection, like 56k modem, GPRS or any other situation where the bandwidth is tight. This is important not only to test how long will those funky pictures take to load, but as AJAX gets more and more used everywhere, you really need to guarantee that everything works acceptably on slow network links, including AJAX calls, timeouts, animations, and everything else that might depend on the server.

There are some graphical applications for Mac OS X that try to do that, but many don’t work as promised. Well, Mac OS X has the solution built-in, you just have to configure it, and that it the internal firewall (ipfw). Since the introduction of Tiger, ipfw can now do QoS (Quality of Service) stuff, and that means you can create channels (or pipes, in ipfw terminology) and control the priority, bandwith, delay and some other settings of those channels. Than, you simply have to pump the traffic you want to see slowed down thru those channels.

I have done two simple scripts to configure my firewall automatically. They will slow down the http port (80) to a crawl. I do not handle the SSL port for https traffic, but it’s easily achieved by duplicating the script content and adjusting the parameters.

Here are the scripts. The first one, that slows down everything:


#!/bin/sh

/sbin/ipfw add 100 pipe 1 ip from any 80 to any out
/sbin/ipfw add 200 pipe 2 ip from any 80 to any in
/sbin/ipfw pipe 1 config bw 128Kbit/s queue 64Kbytes delay 250ms
/sbin/ipfw pipe 2 config bw 128Kbit/s queue 64Kbytes delay 250ms

And the one that brings your network back to normally:


#!/bin/sh

/sbin/ipfw delete 100
/sbin/ipfw delete 200

As you can see, this slows down all your http traffic to 128 Kbps, and introduces a delay of 250ms (which is roughly what you have on slow, modem-like or wireless connections). The delay can really impact the performance of your application, specially if you do a lot of requests to get information to display on a web page. You can change the values accoring to your needs, and even bring the 128 Kbps down to 56 or even less if you have the guts! I still don’t know exactly what infuence is caused bu changing the queue size, but the 64 KB value produces a nice result.

Don’t forget, if you are just making a static web page, for this to work, you have to turn on the apache web server (the easiest way if to turn on Web Sharing in the System Preferences “Sharing” panel) and browse your pages thru it. Direct file access won’t naturally be affected by the firewall. Also, this slows down ALL your http traffic. Not only the traffic that comes from your computer, but also the traffic that comes from the Internet. So, if you are wondering why is your net really slow, the solution is simple: you forgot to run the second script after finishing your tests! :) If for some reason you cannot recover the normal speed, just reboot and your Mac will be back to normal.

To run this scripts, just copy them to two text files (using any text editor that saves in TXT - RTF won’t do it!) and then make then executable (using the “chmod u-x ” command on the shell). Also, you have to run then as root (using “sudo” or switching to root before running them).