Transcript of Kyle Cordes's Java Scripting Talk
http://kylecordes.com/2006/11/10/java-scripting/
Kyle Cordes:
I did something very unusual and bold for my presentation; I did not
use PowerPoint at all. Instead, I took words and I put them together
into sentences and I put the sentences together into paragraphs. I made
them very short sentences and short paragraphs and I put them on a
piece of paper to hand you! You can look at the piece of paper. It's
like having notes without having to take them.
If
anyone does not like the point size, I have a small handful of them at
a larger point size. I have more if that's not enough. I printed 30 and
we have 18 people here. Does anybody want the large print edition?
Raise your hand if you want large print. Would you hand him a large
print? There's three or four in there, not the whole thing, just one
copy.
I picked up that idea of putting words on paper instead of
using PowerPoint from Edward Tufte. If you've heard of him, go to one
of his classes. If you haven't heard of him, go Google him and then go
to one of his classes.
I'm Kyle Cordes. I run a little company
here calls Oasis Digital Solutions. We do a lot of stuff that includes
Java stuff. I put my website on there so I don't have to show it to
you. I'm going to talk about scripting your Java application. There's
about 50 ways you can script your Java application, so I just picked a
few of them that seemed most relevant and I'm going to demo those few
that seemed most relevant.
The history of scripting: to go back
to when people first started doing much with Java, there was this thing
called the Beans Scripting Framework, and it's currently part of the
Apache project. It started out as just some guy who started writing it,
then IBM and a couple of other big players adopted it, and recently
it's been handed over to Apache. So, somewhere around 1997 or 1998,
real early on, there was this mechanism for binding a few scripting
languages into Java. It was primarily oriented towards JSPs, so if you
wanted to do JSPs, for whatever reason, you didn't want to type Java
code in your JSPs, you wanted to type TCL code; you could type that
instead of using this Beans Scripting Framework.
I probably
wouldn't pick it up and start using it now on a project with today's
technology, but a lot of us are stuck with the year before last year's
technology because it's really a big deal to upgrade your Java and so
on. BSF actually works very well, so it's not a bad idea to have it in
your toolbox if you wanted to add some scripting. If your project is
stuffed in JDK 1.4, it's going to work just fine.
So, let's see
how it works. I choose JavaScript for my scripting language whenever I
have the choice just for simplicity. I've tried to show here,
basically, to give you a feel for the minimum amount of code it takes
to get anything to work with the BSF. These lines are simply because I
was too lazy to do the research to find out the magic BSF incantation
to not have this line in here. This line is a language specific line.
If this was not here, then the only thing in here that knew that this
was JavaScript would be this little.js on the end of the file name.
So,
this is a bit of junk just because these versions were in flux, and I
couldn't figure out what the right little bit of API was in my version
to get rid of that. The important stuff is in here; you can go to this
BSF manager and say, "Based on the file name, figure out what kind of
script I have." Then you can get one of these managers and say, "Give
me a scripting engine for that language." In this case it's JS, so this
code figures out that this is a JavaScript file. This loads the right
scripting engine for that. The nice thing about this Beans Scripting
Framework, it's a scripting framework and is it doesn't care what
language you're using, so you could have just as easily said.tcl, I
wouldn't have to change any of the Java code, aside from that thing in
the engine.
This command is just a class I wrote to have
something to call things in, and that is right here. The idea here is
that I wanted to expose some commands that my script could call, and I
made up a whopping one command that just prints out whatever you call
it with.
This declare bean is the syntax for telling the
scripting engine about an instance. So this instance, I'm saying, "Take
this instance and put it in there with this name," this is what that
will be known as inside the script. The interesting thing about this is
that the Java -- this is a Java object -- could be any real class in
your application, and you can expose it to the scripting with this
line. This loads in the script, and this basically reads the script and
processes it, and this calls some functions that are inside it. Let's
see what that script looks like.
Here's the script; it's very
simple. This declares one function. You call it and it calls the right
method on something called commands. The neat thing about this is that
you could make this look however you wanted for your application. If
you were writing an application -- what's your application do?
Audience member:
Insurance.
Kyle:
If you needed to expose an insurance policy, you could make an
insurance policy object and you could use this line of code right here
to tell the scripting mechanism about the insurance policy object. Then
over in there, you could say something like "Policy." and call methods
on it.
How
many people know JavaScript or know a little bit about JavaScript? How
many people have never seen JavaScript before? So, there are a few that
are in neither category. I chose JavaScript because it's pretty close
to Java. In a deep way, it's nothing like Java, but in terms of just
understanding what a few lines of code mean, if you know Java, you can
pretty much guess what these lines of code mean.
Let me run this
thing and let's see what pops up. I guess I had to unmaximize. There we
go. So, there was this line of code in the script, it ran and resulted
in something being printed. There you see a round trip of some data in
and out of scripting.
That's a short tour of what the Beans
Scripting Framework does. Next, I wanted to talk about BeanShell.
BeanShell has got a neat history; I mentioned it in here. A guy named
Pat Niemeyer, here in St. Louis, wrote BeanShell. Somebody said he used
to come to these meetings, like five years ago.
Audience member:
He presented at these meetings three times, I think.
Kyle:
Yes.
Audience member:
He comes once in awhile.
Kyle:
Once in awhile?
Audience member:
He used to come quite regularly when this started.
Kyle:
Right. I remember when the group just started and they met over at
Novell, and it was an amazing crowd of people. I think one time I
estimated it was well over 100. [audience talking] You miss the
popcorn? They had a nice facility over there.
OK.
BeanShell does a lot of interesting things. It's a superset of a Java
language syntax, so any valid Java is valid BeanShell, but there's
other stuff you can type in BeanShell that's not valid Java. Well, the
reason why BeanShell's interesting to me is because of that first
property. So I'm going to show you how that works.
Let's say we
had an application. And notice this is even shorter. It takes just
astoundingly little work to get your application to connect to
BeanShell. You make an interpreter. This is a BeanShell object, this
interpreter. Here, let me make it more obvious, what we're looking at.
You make an interpreter. That method here, this is the BeanShell syntax
for this thing in the Beans Scripting Framework. So you can tell the
Beans Scripting Framework folks kind of like complexity, and Pat really
likes simplicity. I really like this code here; this is nice. Source
means hold in the contents of this and run it. So that's it. These
three lines, this one and these two, are the only thing in here that
was needed to connect to BeanShell. We published one Java instance,
frame, into BeanShell, and let's go see what's in that BeanShell code.
So
that should look an awful lot like Java. Notice anything different from
Java? Anybody notice anything, how this is not Java? [audience
response] Yeah, we didn't have to clear those buttons - they just
worked. Notice anything else different from Java? [audience responses]
That's right. Anything else different from Java? Actually, I'll point
one out. This just says south center, instead of having to have the
type set for it. So there are some shortcut stuff in there that wires
things up, so that that works. So now we have a program here, a pretty
simple program, which is written half in Java and half in BeanShell,
and it works. Pretty simple.
Now the reason I chose BeanShell to
be one of the few that made the cut here to be worth demo-ing is
because of this, I think this is a really important property I'm about
to show you here. BeanShell is very close to Java, so I can take
something that I got running with some configurable BeanShell
mechanism, and I can then shove it right in the Java code. Now it's not
quite right because I will have to bring it back so it's really Java.
Call this button 2. OK. So now I'm going to disable the BeanShell part.
And look - it still works.
To make the point more forcefully, I
could take - here, let me put that back. Here's our scenario: We have a
working Java application where we have a bunch of stuff hard-coded, and
we're in a really big hurry. We need to make it seek and dynamically
tweak something about this program, and we don't have time to think
about all the different complex ways we should make it dynamically
configurable. Let me show you the ultimate shortcut cheat to make it
configurable. You take the Java code that shows your configuration,
which is just hard-coded. You put in a few lines to wire up BeanShell,
and you take that code out of that and put it in to the BeanShell
instead. And now we took something that used to be statically compiled
and it should just work. I going to stop for a second. Does anybody
have any questions about what I just did there and why it's
interesting? Because it was actually so few steps that it's hard to see
what's important there. Anybody want to volunteer anything? Comments?
Nothing.
The next one we're going to talk about is Groovy. I'm
not going to say much about Groovy. I'm not really a fan of Groovy.
Groovy has really one key point, which is that it's a scripting
language but it's not a very dynamic scripting language, so it's
particularly amenable to being compiled into Java. And so it's likely
that it'll run faster and be a little bit more native Java feeling. But
there's going to be a Java talk next month, you say? Next month? So,
Groovy. Yeah, I'm sorry, Groovy. So I won't talk much more about
Groovy. [audience response] It's much more of a scripting language.
It's dynamic and all the sort of things you'd like it to be and
duct-type and all that. So, I mean it's not Java, but it's more like
Java than any of the other common scripting languages.
Now I'm
intentionally showing you how this stuff works before I talk about what
a scripting language is, so you'll have a little bit of frame of
reference when I talk about it. OK. So now the big one. The JSR 223.
It's been floating around for about two years, I believe. And that's an
effort that someone started - I don't remember where. I read where but
I don't remember. [audience response] Sun and Zend started it. OK. They
figured out that a lot of people were doing a lot of scripting stuff
with Java, and that it would be awfully nice if there was something in
the Java box. So they came up with a JSR, where I believe the goal from
the beginning, if I understand the history right, was to get something
that could end up in the JDK. And that actually has happened. So this
new JDK which is shipping December 7th, which is about a month away,
will have Java script in it.
So I'm going to give you a demo of
the technology that's going to go in it. But if I was giving this demo
next month, or if I had the beta JDK on here, I could actually give you
this next demo without having any jars. You see here, I have various
jars in this project? I wouldn't need any of these with this new JSR
223 stuff. So JSR 223 is a more modern API that does the same thing as
Beans Scripting Framework, but it's a little more polished, little
easier to get things going, and it ships with one implementation
already in it. The one it ships with is an engine called Rhino, which
is just a funky name over at the Mozilla project for a Java script
interpreter written in Java. But that one is about to be elevated to a
very premiere place in the scripting world by nature of being in the
Java box, in the JDK that you download in the JRE that you install.
So
I have a quote on here you might enjoy reading. I won't bore you by
reading it to you, but the essence of it is that you won't be fired for
using the scripting that's in the Java box. It's not some funky thing.
The guy talks about if you pick up Joe Bubba's scripting language and
start using it and it breaks, that could be something people get really
unhappy with you about. But if picked up the scripting language that
came from Sun as part of the JDK, it's a highly defensible thing to do.
It's not really important to me, but in some environments it's strongly
encouraged to use things in the box if you can.
So to
demonstrate this one, I took some demo code that I wrote for another
talk here last year and I added a little scripting code to it. So, was
anybody here last year for my talk about Java Swing stuff? You were
here. Anybody else? So very little group overlap. So you make one tiny
change to put this back. So what I'll run in a moment here is exactly
what I ran last year. This is a thing that stimulates the situation
where you want users to rearrange the order of some things, and it lets
you drag and drop their order. And it does this kind of timing stuff in
the background to get this smooth animated reordering. So you can say,
"Oh, I want to grab this guy, bring him up into this position. This one
brings him down to this position. This one brings it up to this
position and so on."
It took about, I think about, twenty-five
minutes to go through the code of how all that works so I am not going
to do that now, but if we have time at the end and somebody wants a
shorter tour of how this Groovy code works I can maybe give the ten
minute version. The important thing about it is that this is just some
demo code. This is all random made up data for the purpose of being
able to demo and it's all hard coded into this demo code. What I wanted
to do was show, let's say you had a working program that did something
interesting and you wanted to add a dynamic hook to it. You wanted to
be able to add run time without re-compiling and without deploying a
new version. Put in some kind of business rule and I will talk later
about business rule scripting. I just wanted to show some business rule
scripting. So I created one extra class which I called a scriptable
ordering widget and I will show you the code in that in just a moment.
To
demonstrate the Rhino API, so this is calling Rhino, which is the
Mozilla Java script engine that is going to be in the box in the next
Java. But, I am calling it with its API. I am not calling it with the
JSR 223 API because; I did not feel like putting the 1.6 data JDK on
here for one demo. I apologize for if anybody really wishes they could
see that. You can go to the site who's URL I put on the paper if you
want to see what the difference is in the URL, but it's very similar.
It's essentially similar to this but things are named different things.
OK, so something you will notice about this is that even in a big font
in a low resolution screen, it all fits on one screen. The point that I
want to make as many times as I can is that adding scripting support to.
An
application is usually much easier than it seems like it is going to
be. We make a context, we make a scope these are details of Rhino. We
have a name for the file where our business rule is in this Java script
run time scripting file. Then I added this hook.
Now, if we have
time at the end I will show you what this hook is called in terms of
the Groovy, but what its purpose is, is to ask the question, "Can I
drag this work order panel?" So when I ran the thing each one of these
things you see this thing here is the work order panel and before the
Groovy lets me drag it; it calls this to ask if this panel can be
dragged. So, again I went through sort of the minimal motions to ask
the run time loadable script if it can be dragged. I read in the script
to a file to save it. I went and got the work order out of that panel.
The interesting thing about the work order is that the here is the work
order. This has nothing special about it, there is nothing script about
this. This is like the plainest Java class you can stumble across. It's
just that and so on. It has get urgency; it has a get address, that
sort of thing. So I get one of those, I make that incantation to stick
that right here into the context. I put that where the script can see
it. This line basically ran the code that I ran. So, I run the code
that has the script text in it. I shove in the objects that I want it
to be able to see. So I say, "I kind of give you the hook." Here I am
giving the script a hook into my program and then I call a can drag
method in the script and then I decided that I wanted the calling
convention for this demo to be that the script function would return a
string. If referring to the string OK then it's OK to drag the thing.
So now there is some way that I could have... It has the feature of
being able to like convert a Java script or a pickled script into a
Java script but I wanted to point out that its very common when
building a scriptable system to have some kind of adaptation between
what kind of API you expose to the script which in this case is a
string based API verses a what your application uses internally. Those
do not need to be the same. Here they are different. I put in the demo
code exception handling, try not to do this in your real apps.
So
let's look at that script. The function can drag; check to see it the
order's urgency is one. Now this is not a Java code because, this is a
Java bean. The bean will say "got urgency" on it and Rhino knows how to
map between Java script and Java enough to say, "Oh, got urgency I am
going to call get urgency to get that value." So that the urgency is
one it's going to be OK otherwise its going to be no. So let's run it
with that scrip in place. I will take one of these that are a four and
I will try and drag it. It's not un-clicking here because you can't
hear and it won't let me drag it. I got to a one and it will let me
drag it. So I now have a run time's scriptable way of saying that one's
are dragable and others are not. And just to demonstrate that its run
time I am going to change is so that two's are scriptable, so two's are
dragable. See my one won't drag anymore and now my two will drag. So
without restarting the app., without restarting the applications surfer
cluster, that is the thing. We have just an immediate run time
straightforward way of making a change. Depending on how the time goes
on at the end, I might explore this a little bit more after I have
talked about some of the background behind it.
So here is where
I wanted to talk about our experiences. The reason I picked this
approach to show, like what is the most interesting demo, is that this
is what we are using at my firm. We are using this Rhino engine. We are
using Rhino's API, which is what I showed here, so we have lines of
code very much like theses this context thing, this standard object
thing. It's running, it's in production and it works great. Our
experience is almost entirely positive. The only slightly negative part
of our experience which you can probably guess is that there are a few
people on the project who have never been exposed to Java script before
so they find it a little bit jarring to have that other language in
there. They find they are trying to de-bide some behavior or something
and they find out "Oh that behavior is actually in the Java script
scripting. I don't know Java script yet." And that is just kind of the
general purpose caveat whenever you have a multi language project. We
have had that same issue with Java in sequel. You can have a developer
who is up to speed on Java, knows it really well, his the problems with
the problems with the sequel, same issue "Oh I have got to stop and
learn the sequel before I can work on this", that sort of thing. But,
other than that it has been just a very positive experience having this
script ability. OK, so three little demos and now I have a little bit
more to bore you with talking.
Let's go back to the little
light. OK, so what is the scripting language? I dug around what exactly
a scripting language is and it seems to be that its one of those things
where people say they don't know what it is, but they know it when they
see it. There are six pages on here of what a scripting language is and
it says there is really no clear definition of what a scripting
language is. So what it means to me is that it's a dynamic language,
meaning that you don't have to do this manifest typing thing. It's a
language where you can just load up some text and run it. For example:
In Java you can't do this right. You can't just load some Java from a
file and then say, "OK, run that now." In the scripting language you
can do that. Yeah, go ahead:
Audience member:
And in terms of part of the difficulty of defining it is that I think I
heard somewhere that in Java 6, which includes scripting, they make
Java itself available as scripting language. Which automatically calls
it compiling.
Kyle:
If you have the compiler present you can to that.
Audience member:
Right, which many production systems don't.
Kyle:
Actually that is a good segue to mention, I mentioned something on the
sheet here. When I showed you the BeanShell, you can just take Java
code and run it in BeanShell. We have not done this yet on a real
project. We did it on a little play project. For the especially large
project, for a project beyond a certain number of classes or lines of
code, you'll have a smaller download if you download the source code
and run it with BeanShell than if you compile it. Because the Java
compiler, the class file for it, is really, really bloated. I mean it's
big. In most languages, certainly in my experience with C, C++, and so
on, when you compile and you get files that are smaller than the files
you started with. That's how compilation is typically done for most of
the history of computer science. But with Java the class files are
often noticeably larger that the source code you started with. So if
your compile results are bigger than the source code, beyond a certain
size, shipping the source code with an interpreter is smaller than
shipping the compiled code. I'm not sure that I would recommend that in
too many situations, but it is certainly an interesting thought
experiment. It has some advantages -- think about eliminating the
compilation step, just take your source and run it.
So,
what is a scripting language? I said, "Interpreted execution." It's a
common answer. I listed some common answers on here. But that's not
really required. There are scripting engines which do this Just In Time
compilation just like Java does. I think Rhino is just an interpreter
at this point. Do you know? Is there any JIT in Rhino at all or is it
purely an interpreter?
Audience member:
No, it's a pure interpreter.
Kyle:
A couple of the other scripting languages like what you're talking
about actually do a JIT, so they are scripting, you can take some text
and run it, but the thing you run compiles it, like in the Java class
files which your JVM JIT compiles into machine code, so you have some
text and now you are in machine code a half second later.
Audience:
And most interpreters parse the code, make it a syntax stream, and then
evaluate that, interpret that, which should remind you of the original
Java.
Kyle:
Which is how Java ran. So like early in a scripting language's
adoption, it's likely to be purely interpreted. If it gets popular, and
people start throwing money at making it fast, eventually it will
probably wind up JIT compiled, just like Java and the.NET languages and
so on.
So,
why would you want to script? We asked ourselves that many times when
we were scripting because we were a little bit afraid to add another
language and add this complex powerful mechanism that people might use
it to make really hard to understand programs. Some of the reasons we
came up with are these that I discuss here.
One is that -- and
I'd like to hear if anybody else that shares this experience: no matter
what kind of configuration mechanism you build, you always find that
the configuration people actually want doesn't work very well in that
configuration mechanism. So you find that either it doesn't have a way
of expressing a certain kind of data, or let's say you build a
configuration mechanism where you build main value pairs, like A=2,
that kind of thing, or like a list of values, two columns, X and Y. But
then the user comes up and wants to enter a whole range, and the only
way to express that in your configuration mechanism is to go into
Excel, do some manipulation, and put 25,000 rows in your config file,
that kind of thing, that happens to me a lot, things of that general
nature.
Well, with scripting, you can say, well, rather than
supply the configuration, you can say, supply a little bit of script
which creates the configuration. So if you want to do something like
use a loop in a config file. Has anyone wanted to use a loop in a
config file? Let's see, a couple of headshakes. Well, if you use
scripting for your configuration, you can use a loop in your config
file.
A real good trigger that if you ought to think about that,
is if you find yourself creating programming constructs in your
configuration file. So lets say you have an XML configuration file for
something and you find yourself putting a "for" tag or a "while" tag or
some kind of a loop tag, that is screaming at you to go pick up
scripting instead. It'll be a lot easier; you won't have to write it. A
rule to live by is: don't program in XML. XML is a really bad syntax
for any programming language.
Another part of why script -- I'm
going to take a poll. Raise your hand if you work on in-house systems
at a company? Raise your hand if you work on commercial systems that
ship to many customers? So, for the purposes of my audio recording
here, it was 18 to zero.
But, if you work on commercial systems
-- I work on commercial systems -- and if you work on commercial
systems, what you find is that you're never done. Because each customer
of a commercial system asks for customizations of all kinds of
behavior. But what you can use scripting to do is: you can use it to be
done. You can divide your system to some core functionality, and then
some add-on functionality. You can define the add-on functionality
using the scripting mechanism, so then you can get your core program to
be completely done. You fix bugs, but you don't customize it per
customer. If a customer comes up with some funky rule where they want
to make a certain field required only if another field is greater than
seven, you don't go editing your source code. If your systems look like
If Customer_ID = Emerson, then... You know, that kind of thing.
Instead, you put that in script.
I mention that mainly as the
in-house commercial divider, if you are in an in-house situation where
you are deploying the same in-house system to 27 different factories
around the country and they each have little custom rules, you can get
to where scripting to provide installation instance specific rules
makes sense.
Actually, I'm going to take another poll. How many
people work on software where the instance or instances of it are all
installed at one physical location? How many people work on software
that is the other way, where work on software in your group that is
deployed to many physical locations? That was more like 50/50. So if
those physical locations are like different business units that have
radically different rules or even somewhat different business rules,
that sort of thing, you might find that scripting is a good way to
accommodate that.
Audience member:
You allow the customers to control the scripting?
Kyle:
I addressed it here, mainly from what you could do. Practically
speaking, when you start out and you go from no scripting to scripting,
it's probably going to be your dev team that does the scripting. But
the goal, especially with commercial software, is to get to where you
can document how to script it. So when you go to document it, what you
want to do -- here's what you don't want to do when you go to document
it -- you don't want to say, first learn the programming language that
we invented here for this project. You do not want that to be on
chapter one of your documentation. What you want is, go learn
Javascript or go learn Tickle, or go learn Joe's off-the-shelf language
that you can go down to Borders and buy books about. And then, once
you've learned that...
Javascript
is great there, because so many companies already have people that know
Javascript because they use it in their web stuff. Now you have a
language, and you know how to use that language, now here is our Object
Model. If you do web development you're familiar with the do Domain
Object Model -- domain, is that right? No, Document Object Model. Well,
you make your own Object Model. And I have an Object Model to show you
here. Here is my Object Model for this demo project. It's one class
that has a few methods on it.
But, you could define, let's say
you're in insurance, you could have 10 or 20 or 30 classes which are
your Object Model, and you can implement Java code to make those things
talk to each other and make them talk to your database, and then you
can write a few lines of code like this one to publish instances of
those to the script that your customers or your customers programmers
are writing. And you can ship a document that says inside your script
here are the things that are available to you. Here are the methods,
here are some test script code which show you how it works, that kind
of thing. And if your Object Model is not too complicated, it's not too
hard to do what I just said. The Document Object Model that browsers
use is really complicated. It's unlikely that you're going to have for
your typical business app something anywhere near as complex as that,
that is really complicated. I think there is a book this thick, just on
the Document Object Model.
OK, the next kind of scripting I
wanted to talk about is... I did a talk last year about an application
architecture which is called Alternate Hard and Soft Layers. Instead of
building your application as one big piece, you build a layer in some
low-level language you create pieces. hen you use a high level
language, a "soft" layer, to wire it together. Well that soft layer it
typically a scripting language kind of thing, so you would use this
code and these things I talk about to implement that architecture.
I
don't want to go into that in depth here because I already talked about
it last year. It's going to be out of scope, but if you ever thought to
yourself, "I wish we could just build some pieces and then reuse them
for different purposes, and just wire them up differently," you're real
close to this hard and soft layer idea, and you would implement that
using scripting. If your hard layer was in Java, then you'd use one of
these APIs to make your soft layer be in one of these scripting
languages.
I threw in a single line comment here, which I think
might be the most important point that I have to say, which is that in
the process of designing your application to be scriptable, to have
some kind of an API that it publishes to mod to scripts, you can't help
but make your application more modular and more testable than it was
before.
Modularity has been known since 1960-something as being
an absolutely critical factor in application architecture, and
testability has gotten real popular in the last five years. Those are
really valuable things, and you might find yourself just stumbling into
them by making your stuff scriptable, by making it possible to write
dynamic pieces that pick up parts of your application and make it do
things.
The other thing I wanted to point out, and this goes
back to that question of who writes the scripts. Do you let just
anybody write the scripts? The answer is if you sell a commercial piece
of software to someone and it has scriptability, then you don't decide
who writes the script. Your customer they decide which of their
employees they are going to designate as the writer of the scripts.
There
are some large-scale experiments out there. I call them experiments, I
mean businesses. Second Life has anybody every played Second Life? I
looked at it for five minutes and I decided my first life was so busy I
don't have time to do Second Life, but nonetheless it's very
interesting. The interesting thing about it from a scripting point of
view is that they violated one of my earlier rules. They made up their
own scripting language. They had hopefully good reasons why they didn't
like any existing one.
Their language is, as custom-made
languages tend to be, a little bit ugly, a little bit rough, not quite
as well thought out as it might have been. And it's used by just people
out there who come to their website and sign up. It is amazing what
members of the somewhat programming-aware public have been able to do.
Sometimes
it is amazing what people do with the most primitive of tools. I have a
story about a client from about ten years ago where we made up our own
really simple, really painful-to-type, impossible to read language. And
they went and used it to create something amazing. They even coded huge
amounts of domain knowledge that runs their entire company in this ugly
language that we created, handed to them, and said, "Can you do
something with this." I'm humbled at how much they were able to do with
such a limited tool.
If you think about the things carefully
that I said you should think about carefully, you'll probably be even
more amazed. When I look at how much they did with an amazingly
primitive tool, I think, "What if I had given them a real language that
they could have gone and bought a book for?" They had to figure out how
this worked from having a few phone calls with me. Imagine if they had
been able to go over to Borders and buy a book about how to use the
language that I embedded in their program. They probably would have
done even more amazing things than they did: the sort of things like
you are seeing happen with user-generated content on Second Life and
four or five other things of that general ilk.
The last major
topic is what kind of API should I have between my application and my
scripting? This is an example of such an API. It's really simple. It's
one class of which one instance is passed into the script and it has
four methods on it. When I say API, don't imagine that means you have
to go learn some new tool for creating APIs. No, the API is just what
Java classes you choose to expose to your script.
I talk about
four kinds of scripting and they each have different guidelines of how
the API ought to work. The first is rule scripting, and that's the one
I did here. Rule scripting is the simplest, most common kind of
scripting you see. Things like "I want users to be able to type in
arbitrarily complex if-then things to figure out how much to charge
somebody on a bill."
Has anybody ever built a billing system?
Ever worked on a billing system? I'm amazed how few people worked on a
billing system. I just got maybe four. I thought everybody had to do
that once in their life. If you write a billing system you find out
that you never, ever get to where the rate-setting mechanism is good
enough for the people who want to set rates. They are perpetually
coming up with new ways of doing tiered rates and discounts and all
kinds of crazy stuff.
A rules scripting mechanism says, well
here's a rate table. I'll give you a rate table that you can type some
rates into, or you can just go into one of the cells of the rate table
and type in a script there. That's a pretty big piece of power to hand
somebody. It might take them a while to figure out, but you can get
from a place where, when business people come up with a new kind of
billing rate rule, you do Java development. They describe it and you
write a little bit of JavaScript (I'm using JavaScript, but you can
write a little bit of Tickle, whatever kind of scripting language you
like) and you email it back to them and say, "I think this works. Why
don't you tweak the numbers in it and paste it into the billing rules
system that's already on your desk, that's already working." That's a
lot happier story if that's happening all the time.
We have a
project that's moving from the first situation to this situation where
right now they send us descriptions of what they want. Sometimes we
modify our core software, but sometimes we just have to edit a script
for them. We're going to get to a point where we only have to ever edit
a script for them. Then we're going to document what that script API
looks like, and then we're going to say, "Here, you can write scripts."
The
key thing about this rules-type scripting is you want a simple shaped
API. Here's the API for this one. You have to have to have a function
called canDrag() and you have to return a string, and if that string is
OK the guy can drag it. That, and then the methods you can call in
order, that is the API in this case. It's very constrained. There are
just a few things you can do. I could write arbitrary JavaScript code
here, but the shape is, in a sense, dictated by that's the entry point
for dragging.
The next kind of scripting is what I call plug-in
scripting. Plug-in scripting is that you want people to be able to add
features to your application in a more end-to-end way. Has anybody ever
said to you, "Hey, for this kind of product we need to collect these
two new fields of data from the user and those two new pieces of data
need to go through the system and come out on the other end." Map that
into your problem domain. That's the common kind of thing that will
happen: an end-to-end situation. I want to add something at the
beginning of the system, I want to do something with it in the middle,
and I want it to come out at the end.
That's a very different
kind of scripting than just supplying one little chunk of code to
answer one question at one point, and that's what I call "plug in
scripting" or even "end-to-end plug in scripting." For that, you have
to do a very different shape of how your scripts talk to your program.
It doesn't look much at all like this, I didn't do an example of it,
because it would be, probably too big to fit in the hour I wanted to
limit myself to, if someone asks a question about it I'll show you a
little more of it. But, the idea is that you may even have a script
that has more than one file. There might be a file that has to go under
the client application, if they're like on a web server somewhere.
There might be a file that ends in some back-end processing system.
There might be another file that, You might have a script that has to
define some customer SQL, the kind of script that says, run this script
in a GUI, run this script to customize the SQL for this customer, run
this script to add another field to the report, you know that sort of
thing, end-to-end. That's a more powerful kind of scripting, and it's
really also a lot more work.
The final kind of scripting, and I
don't have Word or Excel on this machine or else I'd show it to you, is
the big, successful commercial desktop aps, like all the Microsoft
ones, and many of the competing ones, Autocad, even Emacs. It's common
for sufficiently complex apps, for the app itself to be scriptable from
an external point of view also. You can talk to Word as if it's a
software component. You can invoke or instantiate a new MS Word and you
can make calls on it, as if you were clicking on the API's. You can
say, "Type this text in," "Do a file open," "Do a file print," "Do a
format font style," that kind of thing. That is also scripting. It's a
different kind of scripting than what I've shown here. It's also, if
you're building an app that's useful as a component, that's worth doing.
One
example, and I don't have code for it, because its in a different
language and it's a decade ago. I built this little tiny app. Its sole
purpose was to print a mailing label on a certain brand of label
printer, and it was brand of label printer for which you couldn't get a
Windows driver that was any good. So we had to have this little custom
app that you could type text in. Well, we made a way of making it a COM
object. Now if I was doing it now, it would probably be a Java project,
with a Java API, but I was doing it then it was all Windows32, it was
COM. But the idea is the same, that application, I wrote it as an
application and most people used it as an application, they ran it they
typed their text in, it printed out on this special label printer.
Well, later we found a way to use it as a software component, so, that
same application, some other application, which had a plug in scripting
mechanism, (on your paper) called the external scripting mechanism of
this label printer. So, between the different kinds of scripting, you
can say "here are some applications, I throw some script in each one,
and I wire them together and make them talk to each other" Its a style
of Ad-Hoc programming. Which I don't personally find appealing, I like
things that are a beautiful system, not, "I threw in some Word scripts
and it worked." But, there are a lot of systems out there that somebody
threw in some Word scripts and it worked. That's a legitimate and
useful way to create systems of a certain kind in a certain context. If
you can do this external scripting thing, where you expose or write
your applications so that an outside program can get a handle on them
and work with them as if they were a user, that can be a very useful
capability.
So the last thing I want to point out, before I open
for questions, show more details or anything, is to be really careful,
about what you expose, to your scripts. So this has an important
property, the only thing it exposes is, are gets of things like ints
and strings. Lets say I had a work order, a customer, an order, an
address, a whole rich set of domain objects and I expose those to my
scripts, and just one of them had just one method that exposed some
other classes, you could say like "get controller" and it gave you the
web controller behind it, that kind of thing. Anybody ever written a
domain class that sort of accidentally ended up with a "get" that would
give you some like part of the application that wasn't really domain?
Can anybody tell me you have never done that? Will anyone admit to it
here? No one will admit to it here. I will admit to it here. I have
done it, I suspect, some of you're teams have too, although surely no
one in the room actually did it. If you do that, you can accidentally
make your entire class structure, of your entire program, has now
become the API for your script. So you think about what that means,
Let's say this one had a "get web controller" method, and somebody, who
didn't work for you, somebody from some customer side, some branch
office, wrote some code, and they figured out, cause they were in a
hurry and they were trying to meet their bosses needs and they had
something like "order.webcontroller.su" and they did that. Well, you
just accidentally made the particular structure of your application, BE
the API of the scripts, so that when you re-write your application and
you change something about this subcontroller, this method, you will
break those scripts. So that's the caveat, is, be very careful what you
expose to your scripting. That caveat, in the example here, all I had
to do, was not mess it up in here, but, the way some of these scripting
languages work, and I believe the way Rhino works, if you make a one
line invocation to turn it on, maybe even without. You can write code
in here, which I won't show you but it's in the docs, to go instantiate
a Java instance. You know, the class name, so if you're not careful,
even if you don't make a mistake in what you expose in your domain
classes that you hand to your scripting mechanism, someone, who's like,
in the heat of battle, trying to make their problem work, might go
write some code, that instantiates some classes, which you had no
intention of being part of any public API. And they start calling
methods on them, and now, when you ship a new version of your app, you
get in trouble because it broke their script.
Like, maybe they
happen to know you use hibernate so they go instantiate some hibernate
utility class, start making calls on it, and your entire system crashes
because they thought they were doing a good thing. So, my advice is,
build a firewall between how your app works and what you publish to
your script. So you should actually find out how, go find out what the
API is to call some sort of hibernate utility thing here, and go verify
that that does not work, before you release the scripting mechanism for
anyone to use. OK, so that's the end of what I wanted to talk about,
I'd like to hear any kind of questions, or if anybody wants to see
anymore details, about how any of this code works, including just the
whiz-bang GUI thing, that has nothing to do with scripting. We had the
short time, so we're actually done about fifteen minutes early so we
have plenty of time.
Audience member:
Just on that last section, where you were talking about different
styles of using scripting languages, something that I've done, in a
number of applications, is to just provide an expression evaluator,
which many of these scripting agents offer, that is, rather than
jumping out to a completely different script, you let the users type in
a one line expression for something, like when they're putting in a
course, they can describe the price for the course. Many courses will
be, OK its five thousand dollars for you to have this course taught,
and you can send ten people to it, and each person over ten is two
hundred dollars. So you're just putting in an expression, rather than
choosing pricing structure number 6. You let the users type in an
expression, just like they would in Excel, and that's a very simple use
of scripting that is very powerful.
Kyle:
Yeah, and the code for that, if you wanted to do it, you can actually
do that right in here. That is an expression. So, if I wanted to I
could have omitted this. So, this is the line of code that when
unloaded some file and got the system prepared and this is the line of
code that actually invoked this call, this expression. I could have
omitted this, loaded this out of my database and I would have achieved
exactly what you just said, with this.
Next?
Audience member:
It seems like this is pretty powerful but from what I've seen, with a
lot of customers if you give them something that's powerful you are
always going to at least have that one really well intentioned customer
that learns just enough to get dangerous and they are constantly
shooting themselves in the foot, bringing the whole system down, and
making your life a nightmare.
Don't you open the door for that?
Kyle:
You do open the door for it. Which is why I my caveat was so strongly
stated. You need to keep your scripts in a sandbox. Now, even keeping
them in a sandbox users will still find a way to make things bad but
not anywhere near as bad as if they could get out of the sandbox.
So,
to give you an example, if you keep them in that sandbox then in a
sense the worst they can do is write a really, poorly implemented slow
script for calculating the price so that, when they use their product
with their gargantuan slow script, it takes longer to bill that kind of
product. That's not nearly as bad and that's a lot easier to find and
fix than if they get a hook to start making hibernate calls.
Audience member:
Well, I can see that. but I can still see the customer that's always having a syntax error in their price method...
Kyle:
Yep, Absolutely...
Audience member:
And it never works and they are calling you and saying, "Yep, price is not working." So what would you do?
Kyle:
That does happen. That's the trade off right? So, if you build a system
that is very powerful, lets say programmable, now your users are
programmers and they are going to be able to have programmer type
problems. If you only give them a rate look up table they are never
going to have that problem but they are only going to be able to do
simple pricing structures in my ongoing billing example here.
Can you tell I work on a lot of billing systems? [laughter] All my examples are billing systems.
Anybody else? Yeah.
Audience member:
I use scripting in kind of a different way to control a running system.
So, I have a system that's using swing, has 200 some swing beads. I
have exposed creative bindings for all of the swing beads so that I can
use any of them that run my system. I have a filter that is exposed.
Because it runs like that we often have to disable part of the
application. Say, this has to be cut-off from this time to this time
and we just use scripting to be able to control the application for a
time and change behaviors, output them to this, remove it, shut-off
access to this part of the system.
Kyle:
That's more of an operations twist on what I call external scripting
and it's a very, very good idea. The reason it's a good idea is that
it's a lot less work on your part building the system. You can provide
a much more useful interface than if you tried to do it without
scripting. So, if you tried to invent your own way, from scratch, of
exposing the ability to turn each kind of bead on and off. You would
probably have to figure out how you have bugs in it and so on. If you
just take a set of swing beads anyway you can flip a few switches and
say let the script find the swing beads by name and you have the
ability to write scripts, let it run time, call methods and turn things
on and off. That's a fantastic thing to do.
Next?
Audience member:
Can you take classes directly from the script or would you have to have some type of factory class.
Kyle:
That's what I was saying. You can and I don't remember how the API of
Rhino works. You might. It varies by language which things you have to
specially turn something on to enable. There certainly is a syntax. In
fact I think it might be this one. I think this might actually be the
one that turns it on. There are some standard objects, which Rhino
provides which are a Java script API to creating Java class instances
would be what you just said.
But,
I said for sandboxing you really want to hook into that and limit what
the scriptwriter can do. But, on the other side, if you are doing the
kind of operation things then you wouldn't want to limit them because
it's not like it's some user writing scripts, its you writing scripts.
So, there is a power versus risk trade-off.
Audience member:
Can you only make it so you're accessing certain packages?
Kyle:
I don't exactly remember what the API Rhino looks like. The answer is
yes though because if you wanted to you could turn the whole thing off
and provide a factory to the script that will only do the things you
programmed it to do.
Audience member:
I think ultimately the answer to that is you need to use Java security
manager because some of these like Groovy and the other native Java
scripting languages just inherently have all the access to all of Java.
So if you want security you have to use Java security manager and using
that you can completely control what classes you need hit, how and
when. You have to manage it that way.
Kyle:
That's one of the reasons I am not a big Groovy fan. I actually like
more of the law between my system and my scripting whereas there are
benefits the other way too and Groovy gives you the other benefits.
Yeah, you can just type, just like in Java code. Just like in this bead
code I showed you earlier. Yeah, I mean you can do what ever you want,
right? [laugh] Hibernate, whatever. [laughter] So if you shove this
bead shell thing in, yeah, people are going to be able to write code
that just scrapes things up and does things with them.
I
would be much more leery of exposing this kind of mechanism with bead
shell where it's kind of "here's the power of Java" than I would be
exposing the other thing where I said here is a limited keyhole API of
what I want you to be able to call from these scripts.
Anybody else?
Anybody
want the ten-minute version of how that GUI thing works? Show of hands,
who wants it? How many say they want it? Who doesn't want it? Who wants
to be done? It's hard to tell with three to zero. [laughter] I'll show
it briefly then.
[noise] I am recording this on my little $80
voice recorder. Highly recommended purchase for 80 bucks. I am hoping
to be able to slice a little podcast out of the middle of this
somewhere. I don't think anybody would like to here the whole hour of
the podcast but maybe it will be like a five minute segment or
something that is worth posting.
You got to give me a minute to
think about this one since I gave this talk a year ago. OK, if I
remember how I did this I will start with some of the utility stuff;
it's really easy to understand. This thing here just makes random data.
You can tell it to make you a name or to make you an address. So, there
is nothing in here that has to do with the cool GUI. This just makes
some data so that the screen doesn't just say one, two, three on it, so
it looks like real data.
That class we have already seen. It's
just the data holder. It's a bead for work order stuff to hold the data
about a work order, a very simple work order. This is a panel, which
displays a work order. The interesting thing about it is that it has
this highlight and unhighlight method. OK, so if this thing was
running, this is not highlighted... oops, I got to find what I can drag
[chuckle]. That's kind of funny. For the sake of being able to demo
this thing without having our silly script thing getting in the way I
will put this back to the non-scripted version.
OK, so that's
not highlighted, now it's highlighted. It just turns the border yellow,
basically. So, that is very straightforward swing code. makes a frame,
makes a panel, sets up a border different ways and so on. The box
layout, I think this was new in 1.4 or so, it makes it is easier to
stack things up vertically; that's all it does.
What's the next
piece? A palette, this is in the artist sense, a bunch of colors to
choose from. Just made a place to store a set of colors that are
decreasingly dire in their color. So, the one at index zero is red
since it's really important, and so on. You can say, "get a color for a
certain level of urgency," and it gives you the appropriate color.
So
now, I've showed you several classes that are not -- essentially,
there's nothing to learn from those in terms of how this silly GUI
thing works.
What's left? We have a file util, all it does is
load a string out of a file, so it doesn't have anything to do with the
fancy GUI thing. We have main, widget and slots, so that's it.
Let's
look at main. What's it do? You have to trust me; I don't feel like
going through it line by line, this just builds the overall
application. This doesn't have anything in it about the drag and drop
other than that it takes this ordering widget, calls get component on
it and puts that in the center of the layout. This is just boilerplate
make a Java app run code. There's nothing worth pointing out here. This
calls around a data thing.
The way we've all been taught, up
until the last version or so, to instantiate a swing up page, is wrong.
You really need to instantiate all this stuff from inside the swing
event thread by doing this; this is the right way to do it. I think Sun
has made it so that demo stuff always works even if you don't do it
this way. But, you're really violating the rule about only manipulating
your GUI stuff on the GUI thread, you really should. All this create
stuff is often done in demo code, especially -- and actually in a lot
of production code I've worked on -- in the main thread of the app, and
you really need to be doing it in the GUI thread instead. Is that true?
Is what I'm saying true Wichi?
Wichi:
Yes. There are people who encountered problems by not doing it correctly.
Kyle:
Right. It's exceptionally hard to debug.
Audience member:
You're talking about the run later?
Kyle:
Yes. This code just calls that random thing, and it makes a work order
with some random data in it. It just makes this list of work orders and
it tells this ordering widget thing to load those up. There's no code
in here that has anything to do with that drag and drop GUI. That means
there are only two classes to actually talk about that are not support
classes that constitute that GUI, and those are the ordering widget and
the slots.
The
slots. This has a bunch of verbose code in it, but what it creates is
basically this structure: the one, two, three, four, five. It creates a
list of labels, it puts them in the right place and it creates a list
of points where a work order can be located. The labels, this is index
zero of the points. I'll give you a short tour of how that code works.
The
stack size is how many are going to fit vertically, basically. When you
call this set up slot code -- this gets called whenever the window gets
resized -- it figures out if it needs to rearrange where the slots are,
and if so, it keeps running. It loops over all of them, it figures out,
by taking the modular thing, which row and then which column each slot
goes into.
To find the X coordinate, you take what column it is,
multiply it by the size of the column and add a left margin -- very
straightforward. The max X and Y thing are what's the farthest point
that the stuff can reach to. Here, for example, the max XY is somewhere
right over here. The reason that is, even though the point is here, the
width of this thing needs to be at least that wide to fit something in
that column.
The reason it does that is so that by knowing that
size, it can figure out whether the scroll bar needs to be there.
Scroll bars are really easy to make work; you basically make the
underlying component the right width, and the scroll bar just works.
The code to make the underlying component the right width is right
there.
This thing is looping through one through 50. I haven't
configured these little 50 pieces of fake data. It makes a point for
each one. At the points, it takes a label, calls a few things to get
the label set up right and it adds the label to the layered pane.
The
layered pane is worth talking about. A Java -- a J-layered pane, I
think is what it's called -- lets these float a bunch of components on
top of each other and specifies the order. You can tell it, "here's a
bunch of stuff; I want some of them to be in the front and some to be
in back," and you'll see, right here, label layer; the layer, which is
just a number, we put the labels in layer five. If I drag stuff around,
see how the labels are behind? The way they're numbered, higher numbers
are farther back -- or are higher numbers forward? I think Wichi knows
-- you look like you knew.
The point is there's a way of
specifying. If you just put these on a J-panel without this layered
pane, then you wouldn't have any programmatical control of what went
behind what. I wanted to have that control, so I used the J layered
pane, who's API is very simple. When you add something to a layered
pane, you specify numerically what layer it goes in.
What this
procedure does is it says given some point, which slot is that closest
to. Where I'm pointing the mouse, the point I'm closest to is this 22.
Basically, if I'm hovering this here, that's a way of saying if I'm
here, I'm at point 22. That code is isolated from the rest, and it's
kind of a bad algorithm. It doesn't do any kind of topological sorting
or anything. It just loops through all of them and it says that one is
closer than the one I had before, but now that's the closest one.
The
thing worth pointing out is that I called distance squared here. That
probably shouldn't care about this; I guess I don't have my source code
installed. I called that instead of distance because the only thing I
care about in these distances is which one is closer. It's
computationally more expensive to calculate the distance than the
distance squared -- the Pythagorean thing: A squared plus B squared is
C squared. You have to take a square root to get the distance, but if
all you care about is which distance is closer, you can skip taking the
square root.
That probably doesn't matter. I probably couldn't
even find a machine where that matters to this demo. If it can run
Java, it can probably do square roots so fast, you'd never know the
difference.
So, slots have nothing to do with these things
flying around the screen; all this does is defines these points on the
screen. That means that all the logic that has to do with stuff flying
around is all in this, and that's good because that means that we only
have 200 lines of code, roughly speaking, that actually do this neat
thing. This is the one that I'll give a little more VOLOCK to. There's
that layered pane; there's a scrolled pane that it lives in, so that
you can scroll back and forth. We have a timer, which ticks every so
often. We hook up some listeners to it. We put a border around it.
There was some reason why this line of code was necessary.
There's
some kind of a wrinkle in swing about your scroll panes don't work
quite right if they don't have a border. I don't remember the details,
but I remember it had to be there.
I turn on one of the scroll
bars and off the other here. The way I chose to have this scroll is
that it will put few enough columns that it never scrolls vertically,
and it will scroll as far horizontally as it needs to to work. So I did
that there. The usual little bit of glue to find out when it's been
resized. When it's been resized, you'll notice when I resize it, it
changes the number of items in the column? See how there are five per,
now? It calls that code over in that slot class to rearrange the
coordinates. This in essence controls the speed, every 25 milliseconds
it gets called. Is anybody here familiar with EventHandler.Create
already? Who writes Sling code? Does anybody write Sling code here at
any extent, or is it all...? One. Who writes web app stuff? So it's
almost all web here - very little Sling.
This
EventHandler.Create - this is the syntax we have instead of Delegates.
This is the way I'm telling Java that I didn't feel like typing
something like this. I wanted to type less lines of code. So I said,
"Make me a class, which implements the action listener interface so
that I can successfully cast it the action listener and when any method
gets called on this class, call the method drift position on this, this
object." That's what that does. It's a little bit convoluted. Probably
for the sake of demo code I should have done something like this
instead so it would be simpler, but I wanted to show off Event Handler
a little bit. This has been part of Sling since I think 1.3, I think
they started shipping this in the box. So this basically is the hook.
It calls some code every 25 milliseconds to move stuff. Up here,
somewhere I added this thing here as a component, as a listener, there
we go. So in this case I made this class be the listener, so I did
implement these, which means I had to put in stubs for these events
which I don't care about.
So I've showed off three different
ways of Sling event handlers here. There's the anonymous inter-class,
there is the event handler utility, and there is just implement the
interfaces and put empty implementations in the ones you don't care
about. Having done all of these, I didn't like any of them, so I didn't
really feel that I should change all of them, so I left the three in
there to serve as an example.
The mouse has been pressed. Now
we're getting to the good stuff. With that layered pane you can say,
"Give me the component that's at a certain point." So when I do this, I
click, I pressed - the layered pane hadn't handed me back the component
underneath where I clicked, in this line. We're only going to do this
drag thing if it's a work order panel. So I put that in with it. Here's
that hook I use later for the scriptability, so I cast it to the right
kind of panel and I ask if that thing can be drug. Then here we go.
Dragging is an instance variable that keeps track of the one we're
dragging right now if we're dragging one right now. So I say, "This is
the one we're dragging." I tell it to highlight itself. I say set
layer, set layer 20, so higher numbers are closer to you. This way is
up on the Z axis, and so the one that I'm dragging always stays on top
of all the others. When I first did this, I didn't have that, and I
noticed the one I was dragging was somehow sneaking behind some of the
others. So one line of code and it comes up on top. These grab offsets
- those keep track of how far from the upper-left corner I dragged it
from. When I first did this, if I clicked down here, the thing
immediately leapt two and half inches this way. And that was annoying.
That didn't really feel natural. So I figured out that I had to keep
track of where on it I dragged it, and then kind of use that offset the
whole time. So here I am tracking that.
If I release the mouse,
that only matters if you're dragging something. I take the one I'm
dragging. I put it back down to 10, so I kind of put it back in my main
layer, in my layer pane, so that it won't be on top of everything else
anymore. I un-highlight it. I note that I'm not dragging it. I do this
thing called start drifting. This turns on that timer to make that
event get called every 25 milliseconds to move things where they need
to be. Store order is an empty hook. I'll show you what store order
does. It does nothing. This was to illustrate from my original talk
that that's where you would put the code to tell the application, that
this is embedded in, that the order has been changed - you need to
store it back in your domain in some way.
I put this here to
point out a little bit of an annoyance. It seems like double-clicking
is an important-enough thing that it should have a method, some kind of
named way of doing it. I'm kind of annoyed with the fact that you have
to have a click count of two. It seems strange to me. I guess I'm used
to Windows GUI frameworks that treat a double-click as a - that's a
totally different thing than a single click So that's unimportant code.
OK.
This is mouse drag. This gets called by Sling each time - if you're
holding down the mouse button, each time your mouse moves, this gets
called. And there is some limit of how often it gets called. I mean, it
you go like this, it's not being called 10,000 times. It gets called
every so often if you're dragging it quickly.
So if we don't
have anything picked up, we don't have to do anything. So we eat at get
point. This is a way of saying, "Where's the mouse now?" Translate is a
way of saying, "Adjust that point." But what are we going to adjust it
by? We are going to adjust it by the offset of where we dragged that
panel. This calls that slot stuff I pointed out that loops through all
the slots to find the closest one. So what this little three lines does
is, while I am in the act of dragging, each time I move the mouse, it's
figuring out which slot am I holding this closest to. So you can see -
now, you'll see in a minute how the code works - but if notice what
it's doing, it's opening a hole up at that slot. See that? So whatever
I'm holding it closest to, it's opening up a hole there, as if I'm
going to drop it, even though I haven't dropped it. Do a couple of
checks just to make sure that I get a valid index.
Now this is
an unusual piece of code. It's unclear what this does exactly. This is
a list of all of these panels. It says, "The one that I'm dragging -
take it out of wherever it is and then insert it at the index that
corresponds to where I'm holding it. So pick it up out of whatever it
is, so take it out of the list, and insert it into slot 4." So those
two lines of code are what opens the gap at the right place. Then I
take the one that I'm dragging and I set its location to what I
calculated up here. So this is a mouse-dragged event. The Sling
interpreter has no idea that I'm moving this panel underneath where the
mouse goes. This code here is what tells it to move the panel relative
to where the mouse is. I was hoping there would be something built in
to do that. Sling has some drag an drop functions, but they have
nothing whatsoever to do with this. Here's that resize code that calls
the thing to re-setup the slots.
Here's where the action is -
these next couple methods. So drift positions. Let's see where that's
going to get called. Here we go. Start drifting turns on that timer.
When that timer fires, which we saw up here, it calls drift positions.
So that timer fires, here's the timer being passed from the action
listener interface, so it calls whatever that method is called in the
action listener interface, then this event handler makes that call
drift positions. So drift positions is going to be called by the timer
every 25 milliseconds, as long as the timer is on.
So, it's
actually pretty simple. It loops over all the panels, if that panel is
not the one we're dragging. So that's important. That means that the
one that I'm dragging like this, is only being moved by me. And this is
really important to create the illusion that you're physically
manipulating something, is you want all kinds of neat logic to move
other stuff around, but the one that the user has his hand on don't
yank that around under the user's hand. Let the user hold on to that
one. So, that concept is expressed right there.
This figures out
where should this panel be. This is the panel, this is the index of the
panel. The first panel should be in the first point. The one which is
currently first in the list should be right here. That's what that
means.
This is a way of saying, "Get me a coordinate closer than
the current coordinate." So this is the current coordinate. This is
where it should be. This is a closer coordinate. Let's see what that
means.
When I gave this talk I had a much longer chunk of code
here. Somebody in the talk helped me figure out it could be much
shorter, and so here it is. Gap is how far the coordinate is from what
it should be. Increment is how fast I'm going to move it. So look the
speed I'm going to move is related to the gap. So the farther it is
away from where I want it to be, the faster it's going to move.
Let's
see that here. So if I do this, you notice that that one that has a
long way to go, it goes real quick most of the way there and it slows
down at the end. I experimented with three or four algorithms for doing
that before I found that simple algorithm really produced the most
smooth, nice feeling movement.
I love the ternary if operator.
I'm sure some of you work in companies where it's like banned by your
coding standard, but I really like it. So this converted five lines to
three. This is just getting the sign right, basically. It's making sure
if the actual coordinate is here, and if I recorded it here it goes
this way, and vice versa it goes that way. That's what that line of
code does.
Here's the code that called that. This checks to see
if any of them are different. If either of them are different it moves
the panel to that new location, keeps track of the fact that some are
moved, and if none were moved we stop the timer.
I should
probably simplify this demo code by taking that logic out, but I had
already written it so I left it in. Calling the timer every 25
milliseconds if you're not using it, you' probably would never, ever
detect an impact of that on your computer, but it seemed more polite to
stop the timer when we don't need it. Once everything gets to its final
destination, the timer turns off and you're back to a totally static
app. It's not churning anything in the background.
This just
puts them all on the screen. placeWidgets() just puts each one at the
right layer, calls that slot sync to find out where it goes and put it
there. So, this is just setup code. This puts everything at the right
place in the first place, and that's it. The most interesting thing
about this is if you're looking for the code that somehow manages all
this stuff going on, it just doesn't exist. There isn't any code that
understands or manages the totality of it.
What you're seeing is
kind of an emergent behavior of that algorithm that is applied to each
one separately. Every 25 milliseconds, each one of these is its own
little program. Nobody is trying to figure out what should everybody
should do for all time and make a plan to get there. No, each piece
figures out what should I do right now. The emergent behavior is this
whiz-bang demo. That's it.
Audience member:
Just a thought on the turning off the timer. Something that some people
have discovered is carelessly not doing that kind of stuff can
interfere with hibernate mode on your laptop computers. So sometimes
people go to websites and this AJAX thing goes on, and they close their
laptop and find an hour later it's hot and its battery is dead because
there is this AJAX stuff that has been trying for an hour to contact a
disconnected website.
Kyle:
I take it back. You should do the timer thing. That's it. Hope you enjoyed it.
[clapping]
Kyle:
Oh yeah. Let's give away the books.
Audience member:
OK, I need three random numbers in the range from one to 13.
Kyle:
You want me to generate random numbers? Somebody make up a number.
Audience member:
Somebody pick a number.
Audience member:
Eight.
Audience member:
Eight! Rob Widmer. Is that fair?
Kyle:
I'm going to turn the lights on. Brace yourselves.
[Many audience members discussing picking numbers and distributing books]
Kyle:
I'd love to hear some feedback on how anybody liked getting a piece of
paper instead of PowerPoint. What was nice, what was bad, and stuff.
Audience member:
It's nice because you show code up there and you don't have to jump back and forth between code and the PowerPoint slides.
Kyle:
Was it distracting to have this in your hand?
Audience member:
No, actually then you could look at it when you felt like it.
Kyle:
Will you keep this or will you throw it away on your way out?
Audience member:
I'll keep it for a while but I'll probably throw it away later.
Actually, I'll probably set it on my desk and my wife will throw it
away later.
[laughing]
Kyle:
I'm trying to decide how much work it's worth carefully wording things
on it, or whether it should just be an outline. I thought about making
it be just purely an outline.
Audience member:
It let me not take notes knowing that the chief point was there already.
Audience member:
Especially the URLs.
Kyle:
I hate it when people have to write down URLs during a talk. I'm always
too lazy to go to the website to get the slides for something, but if I
had this I might read it the next morning before I throw it away.
Audience member:
Do you normally put the presentations or any information onto the website?
Kyle:
Yeah, we have a website that contains all of the PowerPoint presentations, and one PDF file.
[laughing]
Audience member:
Could you email it?
Kyle:
Yeah, I'll send it. It will be one my site.
[many people talking]
Kyle:
It would be hard to express that in less than an hour. If you go learn
about that, he goes into how to present information well. He really,
really doesn't like PowerPoint.
["he" here refers to Edward Tufte]
[many people talking]