Archive for July, 2005

My first Cocoa App…

Sunday, July 31st, 2005

…built using AppleScript Studio is an unambitious but useful graphical XML-RPC client/debugger. The entire app is written in exactly 78 lines of AppleScript code. If that’s not RAD development…

It’s not much… it lacks polish, and it’s not very full-featured. But somebody out there might find it midly usefull. Basically, the suitably-named “XML-RPC Client” allows you to point at an arbitrary XML-RPC service URL available on the network, specify a method and (limited) parameters, and view the response in a readable display. It’s pretty much meant to be used as a handy, graphical debugger for developers setting up or accessing XML-RPC services. I kinda needed something like this at work, and was tired of using the command line for something for which there should be a nice little graphical tool.

Usage is easy… type the URL of the web service you wish to access in the first field (e.g. http://superhonker.userland.com/rpc2. Type the method name into the second field (e.g. examples.getStateName). Type the parameters in literal pseudo-code in the third field (e.g. 18). As far as the parameters go, strings will generally be recognized as strings, ints as ints, reals as doubles, and true and false as bools. Pretty straight-forward. For example, the parameters tod,27,true,2.3 would automatically be recognized as being of type string, int, boolean, and double. You can optionally enclose strings in quotes, but it will make no difference.

There’s still work to be done. At this point, you can only send arguments of some of XML-RPC’s “primitve” types: string, int, double, and boolean are supported. structs, arrays, base64 and dateTime.iso8601 are not. That means that you may not access any service methods requiring parameters of those types using XML-RPC Client. Release early and release often, right?

XML-RPC Client is freely available, and I’ve posted the AppleScript below. Anyone wishing to help me get support for more XML-RPC datatypes just shoot me a line with the patches.

Dowload XML-RPC Client (124 kb)

XML-RPC Client screenshot

I’ve been tinkering with Cocoa for a couple of weeks now, and I’ve quickly fallen in love. The only other GUI toolkit I’m experienced with is Swing… and while I think Swing is an excellent API, I’m beginning to have a feeling that I’m going to prefer the Cocoa development experience. With Swing, the Java language is the shining jewel of the framework. Java is mature, rock-solid, secure, robust, dynamic and easy to write. In Cocoa, I personally feel that Interface Builder is the defining feature. I haven’t seen a Swing GUI builder that even comes close to Interface Builder. In fact, I’d been so unimpressed with Swing GUI builders in the past, that I wrote the whole idea of GUI builders off as nonsense.

Interface Builder has completely changed my mind.

Wow, you can pull together a fantastic-looking GUI and bind the interface widgets to properties of your Objective-C or AppleScript domain or controller objects in minutes. Again, the Cocoa development experience is fabulous.

Neither Objective-C nor AppleScript really compare to Java as far as languages go, but in the overall experience, this is more than made up for by the ease of development. Overall, I find the Swing API to be a bit more sensible and complete (if not more complex) than the AppKit Framework (the Cocoa equivalent API), but I may be a bit biased and/or uninformed there… Of course, Swing still has the advantage of running on all major platforms…


-- XML-RPC Client.applescript
-- XML-RPC Client

--  Created by Todd Ditchendorf on 7/30/05.

on clicked theObject

    set win to window "main"
    my setProgress(true)
    set theUri to (get the contents of text field "uriField" of win) as string
    set theMethod to (get the contents of text field "methodField" of win) as string
    set theParams to (get the contents of text field "paramsField" of win)
    set theParams to my parseParams(theParams)

    try
        my displayResult(my execute(theUri, theMethod, theParams))
    on error e
        my displayResult(e)
    end try
    my setProgress(false)

end clicked

on execute(theUri, theMethod, theParams)
    using terms from application "http://apple.com/placebo"
        try
            tell application theUri
                set theResult to (call xmlrpc {method name:theMethod, parameters:theParams})
                return theResult
            end tell
        on error e
            return "Unsupported method or URI"
        end try
    end using terms from
end execute

on displayResult(theResult)
    set theView to text view "resultTextView" of scroll view "scroller" of window "main"

    -- hack to convert a record to a string. anyone know how to do this properly?
    try
        theResult as string
    on error e
        set text item delimiters to "Can't make "
        set theResult to item 2 of text items of e
        set text item delimiters to " into type string."
        set theResult to item 1 of text items of theResult
        set text item delimiters to ""
    end try

    set the content of theView to (get the contents of theResult) as string
end displayResult

on setProgress(val)
    set prog to a reference to progress indicator "spinner" of window "main"
    if val then
        tell prog to start
    else
        tell prog to stop
    end if
end setProgress

on parseParams(input)
    set text item delimiters to ","
    set toks to get every word of input

    set len to length of toks

    repeat with i from 1 to len
        set tok to item i of toks
        --coerce bools
        if "true" is equal to tok then
            set item i of toks to true
        else if "false" is equal to tok then
            set item i of toks to false
        else
            --coerce ints and reals
            try
                set item i of toks to tok as number
            end try
        end if
    end repeat

    return toks
end parseParams

AppleScript to toggle screensaver security access

Tuesday, July 26th, 2005

Just as I was leaving the office today, a co-worker of mine provided me with an AppleScript challenge… He had a great idea for a tiny little script that would make his life (and mine, as I suffer from the same annoyance) easier.

Both Cory and I use Apple PowerBooks at work that we take home in the evenings. While at work, it’s nice to have the screensaver require a password to re-activate access to the system. But at home, this requirement quickly becomes burdensome. This feature is controlled by a check box buried in the Security pane of System Preferences, and it’s really a pain to manually toggle this check box twice a day. So Cory said it would be nice to have a script that would effortlessly toggle this check box with a click of a button. Allowing convenient toggling of a significant OS X security measure.

The script below does just that. If you’re interested, I suggest you download the script and save it to /Library/Scripts/ where you will be able to access it easliy from the Mac OS X Script Menu up in the menubar.

This script requires either Mac OS X Panther or Tiger, and also that you enable access for assistive devices in your Accessibility Preference Pane. If you don’t know what that is, don’t worry… the script will guide you through enabling that feature if it’s not turned on already.


tell application "System Preferences"
    activate
    set current pane to pane "Security"
    tell application "System Events"
        try
            tell application process "System Preferences"
                tell window "Security"
                    click checkbox 1
                end tell
            end tell
            tell application "System Preferences" to close the first window
        on error
            tell application "System Preferences"
                activate
                set current pane to pane "com.apple.preference.universalaccess"
                display dialog "UI element scripting is not enabled. Check \"Enable access for assistive devices\""
            end tell
        end try
    end tell
end tell

AppleScript is Easy.

Sunday, July 24th, 2005

Sike!

I’ve been re-reading AppleScript: The Definitive Guide this weekend. I’m fascinated by AppleScript. It’s unusual, quirky, english-like, and widely misunderstood. But most importantly, it’s somewhat poorly documented and therefore ripe for geek exploration and discovery.

Anyone with an interest in programming linguistics should take a little time to read up on AppleScript, IMO.

Here’s a fun little AppleScript I whipped up this evening… With the addition of a simple Rule in your Mail.app preferences, this script uses the Mac OS X speech functionality to give you a verbal alert whenever you receive a new message in your Mail.app Inbox.

Specifically, the script will speak the name and email address of the sender of the newly-received email message. I am constantly distracted at work by the red star that appears on the Mail.app Dock icon when I receive a new message… for some reason I feel compelled to immediately check my inbox to see who has just emailed me. With this script, your Mac will just tell you who’s just emailed while you continue to work (using a configurable voice and rate of speech). Much more convenient.

There are more possibilities with this script, as AppleScript has access to the Cocoa SpeechRecognitionServer which allows you to communicate with you Mac using verbal commands. Maybe I’ll take it further…

Here’s the script’s source code:


set theRate to 210
set theVoice to "Victoria"

tell application "Mail"
    set newMessages to (every message of inbox whose read status is false)
    set unreadCount to (count newMessages)
    if (unreadCount is greater than 0) then
        set theRate to "[[rate " & (theRate as string) & "]]"
        set spokenAlert to theRate
        set i to 0
        repeat while (i is less than unreadCount)
            set i to i + 1
            set aMessage to item i of newMessages
            set spokenAlert to spokenAlert & (the sender of aMessage as string)
            if (i is equal to unreadCount - 1) then
                set spokenAlert to spokenAlert & " and "
            else if (i is equal to unreadCount) then
                -- do nothing
            else if (i is less than unreadCount) then
                set spokenAlert to spokenAlert & ", "
            end if
        end repeat
        say theRate & "You are a fabulous, wonderful individual and you have new mail from " & spokenAlert using theVoice
    end if
end tell

To install:

  1. Copy the source above into ScriptEditor.app, and save it as a script or application to a convenient location on your local disk.
  2. Activate Mail.app and press command-, to view the preferences pane.
  3. Select the “Rules” pane and click “Add Rule”.
  4. In the first drop down box of the first rule condition, select Every Message.
  5. In the drop down for the first action to perform select Run AppleScript.
  6. Click the “Browse” button and select the path for the script you just downloaded.

Big plans for Objective-C?

Monday, July 18th, 2005

Hmm… Apple recently announced that it is ditching the Java-Cocoa bindings, leaving Objective-C as the only officially supported programming language for Cocoa.

This surprised me. Not long ago, Apple replaced Objective-C with Java as the programming language for its WebObjects web app framework/toolkit. This was a very smart move, as tools abound for enterprise web development on the Java platform, while nothing exists in Cocoa or Objective-C.

I was half-expecting that Apple would eventually make the same decision for its desktop apps… keep the cocoa frameworks (particularly their Java implementations/bridge classes), and drop support for the Objective-C language. One clue that I took as a sign: it appears from the Apple Developer Connection Cocoa docs that automatic (mark-and-sweep) garbage collection was planned for Objective-C as of Mac OS X 10.4, but was either not finished or abandoned. I thought maybe Apple started implementing full-blown gc in Objective-C, but decided to skip all that work because they were planning on moving to Java eventually anyway.

Some of you might be scoffing at the idea of Java being the platform for Mac OS X apps… Swing apps are usually noticably slower than pure Cocoa apps on OS X. But I know of one Cocoa/Java app that feels just as “native” and “cocoa-y” as any Cocoa app… Acquisition. With some tweaking by the engineers at Apple, I think this model would be viable for Cocoa.

Besides AHEM, it’s quite possible to create a pure Swing app that looks like Cocoa… it’s just a little time consuming. With custom LookAndFeel help from apple, who knows? But performance is poor compared to Cocoa/Objective-C.

But, obviously, Apple disagrees… they’ve dropped the Java-Cocoa bindings, and don’t appear to be interested in supporting anything other than Objective-C. The message Apple is sending here is one we often hear about Java… Java + server = robust, mature, powerful. Java + client = slow, crappy, ugly.

Now that Apple’s market share is showing significant, steady growth, it’s a good time to be a Cocoa developer. I’ve been reading Aaron Hillegass’s Cocoa Programming for Mac OS X all weekend — I’ll share my Swing vs Cocoa thoughts soon.

Meanwhile, speculation runs rampant about Apple growing into a mass
market company that develops diverse electronics devices. Will Cocoa or even just Objective-C find it’s way onto any of these non-Mac devices? Can someone with deeper Cocoa/Objective-C knowledge add to the speculation here? Obviously, Objective-C outside of Mac OS X is possible.. it’s supported by the gcc. But Cocoa? Can parts of it be reused as a GUI toolkit for devices that don’t run Mac OS X? Or even just provide a model or inspiration?

JSP Taglib Release: JSP 2D Taglib

Sunday, July 10th, 2005

Ditchnet JSP 2D Taglib

I’m proud to announce the release of a little project I recently started…

The JSP 2D Taglib.

The idea for this taglib came to me a while back when I was trying to learn how to embed Rhino in a Java app. If you’re not familiar with Rhino, it’s the Mozilla Foundation’s JavaScript interpreter implemented in pure Java. Rhino has a lot of cool features that extend beyond providing just a basic JavaScript runtime… It offers full access to the Java APIs and libraries in JavaScript, compilation of JavaScript to Java bytecodes, and you can also embed the interpreter in your applications.

That last item is what I’ve done with my new JSP Taglib (check out my other JSP Taglib hotness).

Lots of developers are familiar with the <canvas> tag that Apple invented as an HTML extension a few months back as a part of their Dashboard effort. What apple did was to expose some of the Cocoa Objective-C graphics APIs to JavaScript within an HTML tag named canvas. With this, you could use scripts to draw graphics primitives like circles, arcs, polygons, color gradients and various image effects.

Apparently, the Mozilla guys thought this was a good idea and decided to play along. Firefox 1.1 will include full support for the <canvas> tag.

Anyhow, when Mac OS X Tiger arrived, I took a look at the <canvas> tag docs, and noticed the differences in the Cocoa graphics API that was exposed via the canvas tag vs. the Java 2D API. I was immediately a little annoyed. Great, now I have to learn a new graphics API to use this thing. This really isn’t a valid complaint, as the basics of the API are not hard to pick up, but still… you only have so many brain cells, right?

So that little annoyance quickly spawned a new idea… what about a JSP or JSF taglib that could expose the Java 2D API to JavaScript a la the <canvas> tag? And what if this taglib worked in every browser, including IE? With an open-source JavaScript interpreter (Rhino) available, it should be fairly straight-forward to embed the interpreter in a JSP Taglib, drop a java.awt.Graphics2D object into the JavaScript runtime as a global variable, execute the tag’s JavaScript code body as methods of the graphics object, and then write the resulting image out to the web app’s root directory where it can be accessed from a normal HTML <img> tag!

So, thats what I did. Check it out.

Basically, this taglib allows you to “paint” directly on any web page using JavaScript and the Java 2D API. Sweet!

But wait, there’s more! Just like any JSP or JSP EL expression, the 2D Taglib has access to implicit read-only JavaScript Object represenations of the applicationScope, sessionScope, requestScope, pageScope, request parameters and more! This is where the true power of the 2D Taglib becomes evident…

Imagine for a moment that you work for a web hosting company that has developed a web app to edit your client’s server configurations… Objects representing those servers are stored throughout the life of the application in each of the scopes mentioned above. At runtime, using the 2D Taglib, your application could access the server objects in a given scope and render dynamic graphical representations of a given client’s hardware configuration. Look ma, no Photoshop!

Thread-safe, server-side Blocking Queue implementation in Objective-C

Tuesday, July 5th, 2005

Warning: this post is rated EOCC for Excplicit Objective-C Content… it contains large Objective-C code samples and may not be suitable for small children or developers uninterested in multi-threaded Objective-C applications. Don’t say I didn’t warn you…

Here’s my first (somewhat) non-trivial Objective-C program… it centers around a class called BlockingIntQueue. As it’s name and this post’s title implies, this is meant to be a prototype for a generally useful Blocking Queue implementation similar to what is often seen in Producer/Consumer code samples. Such samples are often the centerpiece of a thread notification example such as this one in the Java Tutorial.

So first, here’s my own implementation in Java 1.4 using Java’s language-level support for thread-spawning and notification (namely the java.lang.Thread class and the wait() and notify() methods of java.lang.Object. This should help to demonstrate what I’m actually attempting here. If you’d rather, you can just skip straight to the Objective-C code.


import java.util.Random;
import java.util.List;
import java.util.LinkedList;

public class ProducerConsumerExample {

    private static final int TOTAL = 40;
    private static final int START = 0;
    private static final Random rng = new Random();

    public static void main (String args[]) {
        BlockingIntQueue q = new BlockingIntQueue();
        new Consumer(q).start();
        new Consumer(q).start();
        new Producer(q).start();
    }

    static class BlockingIntQueue {

        private static final int MAX = 5;
        private static final int MIN = 0;

        private LinkedList q = new LinkedList();

        public synchronized void put(int i, Producer p) {
            try {
                while (q.size() >= MAX)
                    wait();
            } catch (InterruptedException e) { }
            q.add(new Integer(i));
            System.out.println("Producer: " + p + " added: "
                               + i + ", size: " + q.size());
            notifyAll();
        }

        public synchronized int get(Consumer c) {
            try {
                while (q.isEmpty())
                    wait();
            } catch (InterruptedException e) { }
            int result = ((Integer)q.removeFirst()).intValue();
            System.out.println("Consumer: " + c + " got: "
                               + result + ", size: " + q.size());
            notifyAll();
            return result;
        }

    }

    static class Producer extends Thread {

        private BlockingIntQueue q;

        public Producer(final BlockingIntQueue q) {
            this.q = q;
        }

        public void run() {
            for (int i = START; i < TOTAL; i++) {
                q.put(i,this);
            }
        }

        public String toString() {
            return "Producer #0";
        }
    }

    static class Consumer extends Thread {
        private static int nextId = 0;

        private static int nextInstanceId() {
            return nextId++;
        }

        private BlockingIntQueue q;
        private final int instanceId;

        public Consumer(final BlockingIntQueue q) {
            this.q = q;
            this.instanceId = nextInstanceId();
        }

        public void run() {
            for (int i = START; i < TOTAL/2; i++) {
                q.get(this);
                try {
                    // make things interesting
                    Thread.sleep(rng.nextInt(500));
                } catch (InterruptedException e) { return; }
            }
        }

        public String toString() {
            return "Consumer #" + instanceId;
        }

    }
}

This is Java thread notification 101 stuff here… not much different from the example shown at the Java Tute link above.

So like I said, I wanted to try this in Objective-C.

After spending a couple of evenings on it, I have to say that so far I find Java cleaner, more mature, more readable, and more capable than Objective-C. Despite all that, I find myself enjoying Objective-C a lot… I can’t really say what about this language attracts me, but for some reason I sorta love it. There is only one concrete feature that I can definitely say I am missing in Java from Objective-C already: named method arguments. This is a feature that Objective-C creator Brian Cox apparently picked up from Smalltalk, and I am truly living for it already.

So my attempt to implement the code above in Objective-C has been mildly successfull… I have classes for the three main types in the example program: Producer, Consumer, and BlockingIntQueue, as well as a main function where the magic happens.

You can dowload the entire XCode project (1.7MB) if you’re interested.

Let’s look at the main function first. We won’t even talk about Objective-C memory management, so please just ignore anything that looks like it’s related to programmer-managed reference counting (e.g: NSAutoreleasePool instances).


#import <Foundation/Foundation.h>
#import "BlockingIntQueue.h"
#import "Producer.h"
#import "Consumer.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    BlockingIntQueue *queue = [[BlockingIntQueue alloc] init];

    Producer *p  = [[Producer alloc] initWithBlockingIntQueue:queue];
    Consumer *c1 = [[Consumer alloc] initWithBlockingIntQueue:queue];
    Consumer *c2 = [[Consumer alloc] initWithBlockingIntQueue:queue];

    [NSThread detachNewThreadSelector:@selector(run:)
                             toTarget:p
                           withObject:nil];

    [NSThread detachNewThreadSelector:@selector(run:)
                             toTarget:c1
                           withObject:nil];
    [NSThread detachNewThreadSelector:@selector(run:)
                             toTarget:c2
                           withObject:nil];

    [pool release];
    return 0;
}

The main method is actually strikingly similar to the Java code… One producer object and two consumer objects are instantiated all with a refer– er, pointer — to the same BlockingIntQueue. The run methods of the producers and consumers are all executed on separate, newly-created threads using Objective-C’s method selector mechanism. This mechanism basically allows you to specify glorified function pointers. The Producer and Consumer classes can be thought of as implementations of java.lang.Runnable in Java terms.

Here’s the Producer header and implementation:


#import <Cocoa/Cocoa.h>
#import "BlockingIntQueue.h"

@interface Producer : NSObject {
@private
    int instanceId;
    BlockingIntQueue *queue;
}
+(int)nextInstanceId;
-(Producer *)initWithBlockingIntQueue:(BlockingIntQueue *)q;
-(void)run:(id)anObject;
@end


#import "Producer.h"

@implementation Producer
+(int)nextInstanceId {
    static int result = 0;
    return result++;
}

-(Producer *)initWithBlockingIntQueue:(BlockingIntQueue *)q {
    queue = q;
    instanceId = [Producer nextInstanceId];
    return self;
}

-(void)run:(id)ignore {
    int i;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    for (i = 0; i < 40; i++) {
        [queue put:i from:self];
        // why won't this work???
        //[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:.5]];
    }
    [pool release];
}

-(NSString *)description {
    return [NSString stringWithFormat:@"Producer #%i",instanceId];
}
@end

The only problem I had in implementing this class was in putting the current thread to sleep (like Thread.sleep(100); in Java); Can anyone tell me why this doesn’t work?


[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:.5]];

That code seems to cause the current thread to deadlock indefinately rather than than to sleep for half of a second as I would expect from looking at Apple’s Objective-C docs. The whole code sample kinda hinges on this temporary pause, so if anyone has any ideas, I’d love to hear them.

Here’s the consumer header and implementation:


#import <Cocoa/Cocoa.h>
#import "BlockingIntQueue.h"

@interface Consumer : NSObject {
@private
    BlockingIntQueue *queue;
}
-(Consumer *)initWithBlockingIntQueue:(BlockingIntQueue *)q;
-(void)run:(id)anObject;
@end


#import "Consumer.h"

@implementation Consumer
-(Consumer *)initWithBlockingIntQueue:(BlockingIntQueue *)q {
    queue = q;
    return self;
}
-(void)run:(id)ignore {
    int i;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    for (i = 0; i < 20; i++) {
        [queue getFor:self];
    }
    [pool release];
}

-(NSString *)description {
    return @"Consumer #0";
}
@end

That class didn’t seem to pose any problems…

Finally, here’s the good stuff… the BlockingIntQueue class:

The header file BlockingIntQueue.h:


#import <Cocoa/Cocoa.h>

@interface BlockingIntQueue : NSObject {
@private
    NSConditionLock *lock;
    NSMutableArray *q;
}
-(BlockingIntQueue *)init;
-(void)getFor:(NSString *)desc;
-(void)put:(int)i from:(id)desc;
@end

The implementation file BlockingIntQueue.m:


#import "BlockingIntQueue.h"

static const enum queueFullness {
    NONE = 1,
    SOME = 2,
    FULL = 4
};

static const int MAX = 5;
static const int MIN = 0;

@implementation BlockingIntQueue
-(BlockingIntQueue *)init {
    lock = [[NSConditionLock alloc] initWithCondition:NONE];
    q = [NSMutableArray arrayWithCapacity:MAX];
    return self;
}

-(int)getFor:(id)consumer {
    [lock lockWhenCondition:SOME];
    NSNumber *num = [q objectAtIndex:0];
    [q removeObjectAtIndex:0];
    NSLog(@"%@ got %@, current size: %2i",consumer,num,[q count]);
    [self doUnlock];
    return [num intValue];
}

-(void)put:(int)i from:(id)producer {
    [lock lockWhenCondition:NONE];
    NSNumber *num = [NSNumber numberWithInt:i];
    [q addObject:num];
    NSLog(@"%@ put: %@, current size: %2i",producer,num,[q count]);
    [self doUnlock];
}

-(void)doUnlock {
    [lock unlockWithCondition:(MIN < [q count]) ? SOME : NONE];
}

-(NSString *)description {
    return [NSString stringWithFormat:
    	@"I am a BlockingIntQueue containing: %@",q];
}
@end

As far as I could tell from Apple’s Objective-C dos, the NSConditionLock can be used to simulate Java’s built-in support for thread notification via wait() and notify(). But after building this example, I’m not convinced that the NSConditionLock class provides all the necessary functionality. I briefly explored the older C-based Carbon semaphore functions that seem to offer cross-thread notification (MPSemaphoreCreate,MPSemaphoreWait, and MPSemaphoreSignal) but I had serious trouble with those… I have a feeling those semaphore-based C functions will work if I try harder, but I was really hoping that Objective-C would provide some higher-level OO support for thread notification. :( Somebody tell me I’m missing something.

So my Objective-C program runs correctly… the queue blocks consumer objects attempting to retrieve ints when the queue is empty, and adds new ints from producer objects as needed. One problem tho… the queue never reaches the maximum size of 5 but instead stays at a size of no greater than 1 unlike it’s Java counterpart which fills up to the max quickly and then stays there. I suspect this is actually due to the fact that I can’t get the consumer threads to sleep like I want them to… can anyone help with that?