Since the release of the first edition of
In Search of Stupidity: Over 20 Years of High-Tech Marketing Disasters (a.k.a ISOS)
I've been accused by various parties of being an apologist for Microsoft.
Much of this criticism has come from various Mac Fan Boys and deranged
Microsoft haters, and I've ignored them for the very simple reason that they almost always,
without exception, don't know what they're talking about. But the good news for every hardened
Redmond loather is that with the introduction of Windows Vista, Microsoft has indulged in the
type of high-tech marketing stupidity previously chronicled in ISOS I and II.
The Windows Vista launch has been a rolling train wreck, and things have not been getting better.
This article reflects my Vista observations and the lessons its disastrous launch teaches.
The industry's biggest software company has, with its latest release, managed to repeat positioning
mistakes made almost a quarter century ago by MicroPro, which has earned its own inglorious chapter
in ISOS. I'm speaking, of course, of the launch of Windows Vista. Several months after the release of
Vista to businesses (the product was released to consumers in January, 2007) the consensus
of the market has been that Vista is a flop. It's a peculiar type of flop. Financially,
Vista is making a great deal of money for Microsoft. No surprise there; after all, the company
has an almost complete monopoly in the desktop OS and main business applications markets
and the dominant position in the server OS segment. OEMs are in complete thrall to Microsoft;
if you don't offer Windows on your laptop, you've got an unsellable silicon brick in your warehouse
(yeah, yeah, I know about Linux. Not a serious option at this time.).
But that said, Vista has failed to meet any of the lofty goals originally set for it.
It has failed to excite any segment of the market, including key influencer groups such as the
press and the gamers. It is not driving sales of new computers. At retail, the pull figures
for Windows out of the channel are dreary and show no signs of changing (we're researching
these numbers and will be reporting on them soon in an upcoming issue of Softletter).
The blogs are condescending and even many Microsoft devotees are dismayed by what they see and hear.
Legitimate copies of Windows XP (and even 2000!) just became more valuable and knuckles will have
to be severely cracked before the hands grasping those precious old boxes relax and allow a fresh
copy of Vista to be shoved into a reluctant grasp. The fact is, few people see any need or
have any desire to buy Vista.
In all fairness, some of the problems that accompanied the Vista launch are beyond Microsoft's
control. As the Internet has matured as a development and infrastructure platform, the growth
of SaaS and advent of hybrid applications has led to an inevitable challenge to Microsoft's
monopolies in OSs and desktop applications. Over the next several years, Microsoft will need
to execute the painful chore of chewing off its own two strong revenue arms (but not too quickly)
and hope they regenerate into new revenue and profit builders. It's not a tasty task, and you
can't really blame the company for avoiding it, necessary though it is.
On the other hand, it's hard to see how they could do a better job of making clear the potential
advantages of SaaS if they deliberately tried. At Softletter's recent SaaS seminar in Santa Clara,
I felt compelled to add the Vista launch and WGA to the list of factors driving further interest
in SaaS. The chief objections to using SaaS in the enterprise include:
Reliability and service interruptions. Microsoft helped relieve that SaaS concern with the
aforementioned August 2007 bricking of thousands of innocent PCs which were struck down when
logged into a WGA server that had faulty code loaded onto it. It took Microsoft 19 hours to
fix the problem; in SaaS world, a service interruption of four hours can trigger an escrow demand.
SaaS advantage: Check.
Privacy concerns. Microsoft is doing a good job of ameliorating this worry with its increasingly
aggressive software auditing campaigns driven by its ongoing data mining operations which compare
your company's size with the amount of its software Redmond thinks you should own. SaaS advantage: Check.
Pricing concerns (as in, is SaaS more expensive than those perpetual licenses). Since Microsoft's
current licensing policies are for all practical purposes incomprehensible and increasingly more
expensive, SaaS offers (in theory) the virtue of predictability AND knowability. SaaS advantage: Check.
Control over your computing environment. Microsoft helped lay this worry to rest with its
stealth updates to the Windows update system which occurred despite the fact that supposedly
the operating system allowed you to block any such updates. Whoops! THEN the company compounded
the problem by either not realizing or telling people that the stealth updates broke XP's repair
function. THEN Windows started pushing updates and rebooting systems without, it seems,
Microsoft's knowledge! (Since this is the Halloween season, perhaps the update servers have been
possessed by evil spirits?) SaaS advantage: Check.
But paradigm shifts and SaaS aside, the biggest problem crippling the Vista rollout was Microsoft's
complete bungling of the critical task of properly positioning the product. Vista's marketing
identity is a dirty smear in the mind's eye of the public; today, it's almost impossible to
find anyone (particularly anyone from Microsoft) who can cleanly and quickly describe why Vista
is superior to Windows XP. And a confused buyer is a buyer that does not buy
(or one who buys something they can understand).
Microsoft's Positioning Sins
During the Vista rollout, Microsoft committed several positioning sins. Redmond's mistakes begin
with the deadly transgression of creating a positioning conflict within its own product lines.
It's a surprising mistake. During the history of the Windows 9.X vs. NT product lines, Microsoft
was frequently tormented by customers confused by which product to buy, a mistake highlighted
by the firm's creation of one of high-tech's dumbest ads, the two nags racing piece which you
can see preserved on www.insearchofstupidity.com
in the Museum of Stupid High-Tech Marketing.
While 9.X and NT both existed, Microsoft frequently had to explain why a customer should buy one
product over the other when both were called Windows, looked very much the same,
did very much the same thing, and cost pretty much the same. But Microsoft was lucky in that
during this period its chief jousting opponent was IBM and OS/2.
But with Vista Microsoft pointed the lance at its own foot, kicked its marketing war horse
into action, and firmly pinned its toes to the ground. There are no less than six (actually seven,
counting the OEM version. Wait, isn't that eight if you count academic packages? Are we missing
some other variants? Probably. Does Windows CE count?) versions of Vista currently available for sale:
Vista Starter (which you can't buy unless you live in the Third World, apparently.)
Vista Home Basic (which, amazingly, does not include the new UI.)
Vista Home Premium
Vista Business
Vista Enterprise
Vista Ultimate
This plethora of choices leads customers to naturally ask a deadly question: Which one do I buy?
Before, a consumer only had to compare between Windows XP Home and Professional (Windows Media
Edition came along too late in the life cycle of the product to become well-known enough to
confuse anyone). To help customers, Microsoft has published a blizzard of collateral, comparison
sheets, pricing matrices, etc., etc. Thinking about whether to buy Vista Home Premium vs.
Vista for Business? What's Super Fetch worth to you? How about Volume Shadow Copy?
But it's good to know the Business version includes Premium Games. Just what a business
person is looking for in their business version of Vista. Why not include applications that
have some applicability to business concerns? Maybe stock analysis and reporting? Specialized
business calculators? Something? Anything?
And making things ever more interesting is that the EULAs accompanying each version are different.
Want to create a virtual machine on your PC and run Vista Home in it? You can't! How about
Vista Business? You can! Why one and not the other? Who knows.
Moving along down the path of positioning damnation is Microsoft's failure to attach any
powerful or desirable image to Windows Vista as a product line. Try to imagine in your mind
what memorable picture or capability is associated with Vista. None comes to mind.
The product does have a new interface, but Microsoft made no attempt to build a compelling
image of usefulness around the AeroGlass UI. Yes, the icons are bigger and the screen savers
are prettier, but so what? Microsoft might have discussed how the new desktop gave users
X-ray vision like Superman, increasing their day to day productivity while working with
Windows, but it didn't. Vista is supposed to be far more secure than XP, and perhaps Microsoft
could have discussed Fort Knox, an integrated group of technologies that allowed you to
lock up your PC with bank vault tightness, but it didn't. (Given Microsoft's past security woes,
it may have lacked the stomach for this gambit.)
By contrast, when Apple released Tiger (OS X 10.4) the market and the press were bombarded
with information spotlighting Spotlight, an integrated search utility baked into the new release.
Desktop search was by no means new on either Macs or PCs, but the Mac campaign succeeded in
making people aware of its usefulness and, more importantly, gave them a mental picture of
why they might want to give Tiger a look. With Leopard (OS X 10.5), the emphasis was on
Time Machine (integrated backup).
Another key mistake made in launching Vista was to match features to pricing expectations and
here Microsoft has also failed, particularly in respect to Windows Ultimate. Ultimate is the
kitchen sink of Windows, the one with all the toys and whistles and it's expensive at $450
for a retail version (and pricey at $270 for the upgrade version). But not to worry!
With your purchase of Ultimate you're promised all sorts of goodies only you, our ultimate
customer, can download. And what are these exciting ultimate premiums? Well, to date,
they include fun things like Windows Hold 'Em (a poker game), extra language packs
(those squeals of delight are coming from buyers who just unpacked Finnish and Korean)
for the Windows multi-language user interface, Secure Online Key Backup, and BitLocker
Drive Preparation Tool. (The latter two products are included in other, cheaper versions
of Windows.) And oh, let's not forget the new screen saver that let's you use videos.
Alas, it's in beta and not due to be finished for a while yet. Ultimate customers, are,
of course, delighted with all of this.
In its long and storied history, Microsoft has distinguished itself from its competition
by its ability to avoid the self-inflicted wound of stupid marketing. With the release of
Windows Vista, this has changed. During the release of Windows Vista,
Microsoft has repeated mistakes made by MicroPro (positioning conflict), Borland (positioning
conflict, pricing/feature disparity), Novell (positioning conflict), Ashton-Tate (pricing/feature
disparity coupled with inept PR) and itself (Windows 9X vs. NT), proving that the company
now suffers from the same institutional memory hole that afflicts much of high-tech.
The Vista release serves as a valuable and contemporary object lesson in how not to position
and launch a new software product.
When inheriting an Ant build file to maintain, it is useful to get a feel for the flow and structure. Sketching the dependencies between targets is a useful way to do this. While it is time consuming to do so by hand, there are a number of tools to help.
I tried three different tools to generate the output. All of them generate a .dot file which needs to be run through the Graphviz tool to be converted to a useful format. Graphviz can covert to all image formats among many others. Each tool is described below with an example of how to call it. I've also included two sample images: from our build script to Pick Winners in the weekly book/software promotion and a contrived example designed to show how antCall and forEach work.
Ant2Dot - Ant2Dot is an XSLT file that converts your Ant build file into a .dot file. Each target becomes a rectangle and dependencies between targets are shown. The instructions for using it are a bit old and only apply to Java 1.3 and 1.4. They don't work on Java 5/6 without modification. The website lists a "to do" for adding support for Ant 1.6's import and subant features. Similarly, 1.7 features aren't supported yet. Ant2Dot is good for simple build files, but doesn't scale up to handle use advanced features. Running was as simple as downloading a stylesheet and running the command:
Output of JavaRanch's book promotion pick a winner dependency structure and a contrived sample:
Vizant - Vizant is a Java library that creates a .dot file by using a SAX parser on your Ant build file. Each target becomes an oval and dependencies between targets are shown. Vizant supports antcalls and allows you to drill down to generate only part of the build tree. You can also drill down to show part of your build tree. Vizant was easy to use. I wanted the dependency ordering so continued looking for a library to meet my needs. In order to run Vizant, you need to download a jar containing the source and build it using Ant. They provide the build.xml to do so. Since this is a library for Ant documentation, it is reasonable to assume people know how to run Ant. You also call it through a simple Ant build file:
Output of JavaRanch's book promotion pick a winner dependency structure and a contrived sample:
Grand - Grand is also a Java library that creates a .dot file. It takes a different approach and actually creates the Ant directory structure using Ant's Java libraries. It can support anything in your classpath including ant-contrib and Ant 1.6's import feature. The logic doesn't capture the subtleties of Ant-Contrib like the forEach loop though. The graph is a bit more detailed. The default target is a yellow hexagon. Targets with a description are marked in blue so you can show entry points. The remaining targets are shown as ovals. You can also drill down to show part of your build tree. You can use a Grand UI tool instead of the dot file, but I wasn't able to get the UI. Like Ant2Dot, dependencies are numbered. Grand was as easy to use as Vizant. They provide you with a jar for download. You call it through a simple Ant build file adding any other jars used to the classpath:
I almost missed the boat with this one, heck Bear and company managed to get a book out before I decided to give it a serious look, but finally I have looked.
Introduction
As a quick introduction, Prototype is a JavaScript library that has become quite popular due to the things others have built on top of it. I like to think of Prototype as an extension to JavaScript and inclusion of various best practices. Through it code becomes less cumbersome and unwieldy, and therefore easier to manage. This doesn't sound like much, but I have developed an aversion to JavaScript over my eight years of casual development and Prototype made me much happier. The only Prototype functionality used directly in the following is the $() function, which is a sort of shorthand for document.getElementById(), the rest of Prototype is used under the covers by Scriptaculous. Two additional resources I liked were the online API book and the article How well do you know prototype.
Finishing the introduction, Scriptaculous or script.aculo.us, as it is commonly written to match the URL, is written as an additional layer on Prototype and "provides you with easy-to-use, cross-browser user interface JavaScript libraries to make your web sites and web applications fly". There are few different areas that are enhanced by Scriptaculous, but we're just looking at the animation effects, and the smallest subset, at that.
The Scriptaculous Demos
The demos provided by Scriptaculous show some useful effects, but I will just be looking at two. To get started you need the libraries, but you can get scriptaculous.js and effects.js in the Scriptaculous download, and it contains prototype.js too. It contains some other files too, but we just need the ones listed.
Time for some code. Create a file called test1.html with the following content, make sure the javascript files are in a folder called javascript in the same location.
Clicking on the blind-down demo creates a 'BlindDown' effect on that div, same with the blind-up except that it also has a little event that fires off afterwards to show the div again, otherwise we lose it. The code is pretty simple: in each there is an outer div which can be ignored, and an inner div of interest. The two active div's respond to an onclick event, the onclick calls a Scriptaculous effect and passes a reference for the div to Scriptaculous to provide the effect. Hence: onclick, Scriptaculous, 'this', done.
Extending the Demo
Hopefully the example above is pretty self explainatory. To add our own functionality we will add the BlindUp and BlindDown effects to a single div, allowing us to open and close it. Rather than applying the onclick to that div, we'll add it to a second div so that we always have something to click on, even when our 'blind' div is not available. It will start out something like this:
<div class="header">
Header
</div>
<div class="bodyOpen">
This is the body part,
which will do the
blind-up and blind-down
effects
</div>
Header
This is the body part, which will do the blind-up and blind-down effects
Unlike the previous example, where the click on the div caused an action on the div, we are going to make a few changes...
The onclick event will be on the header, but the effect will be applied to the body
We will use naming rules in our element ids to make the logic and control easier
We're going to use both effects, BlindUp and BlindDown so that we can open and close the body
We will use CSS classes to track the opened or closed state on the body
Naming the Head and Body
Element IDs must be unique. This isn't my rule, it's an XHTML rule. Since you're not allowed to reuse an ID after using it once, it makes them handy for identifying and finding specific items on our page. On our page, we are going to assume that whatever ID the header has, the associated body's ID will have 'Body' added to the end. Hence the associated body for header 'itemOne' is 'itemOneBody'.
Tracking the Body State
The Prototype $() function, mentioned earlier, accepts either an Element or the name of an Element ID, so starting from a reference to the header element, the header ID is header.id, the therefore the ID for the associated body is header.id+'Body' and to get a refernce to that Element we call $(header.id+'Body'). Now we want to know if it has the CSS class 'bodyOpen', and again Prototype comes to our aid with hasClassName('bodyOpen'). There is also a function to add a class name addClassName but I'm going to keep the name of the function used to remove a class name secret for now.
Putting these together, we can write a function which takes the reference to a header as its argument, detects the state of the body and then performs a switch on the class state.
function toggle(header)
{
if($(header.id+'Body').hasClassName('bodyOpen'))
{
// open
$(header.id+'Body').addClassName('bodyClosed');
$(header.id+'Body').removeClassName('bodyClosed');
}
else
{
// close
$(header.id+'Body').addClassName('bodyClosed');
$(header.id+'Body').removeClassName('bodyOpen');
}
}
Adding the Effect
As we saw in the Scriptaculous demos, adding an effect is easy.
function toggle(header)
{
if($(header.id+'Body').hasClassName('bodyClose'))
{
// open
new Effect.BlindDown($(header.id+'Body'));
$(header.id+'Body').addClassName('bodyOpen');
$(header.id+'Body').removeClassName('bodyClosed');
}
else
{
// close
new Effect.BlindUp($(header.id+'Body'));
$(header.id+'Body').addClassName('bodyClosed');
$(header.id+'Body').removeClassName('bodyOpen');
}
}
Trying it Out
All that remains now is to add out IDs, plug the function in to the div and give it a try:
<div class="header"
onclick="toggle(this);">
Header
</div>
<div class="bodyOpen">
This is the body part,
which will do the
blind-up and blind-down
effects
</div>
Header
This is the body part, which will do the blind-up and blind-down effects
Now To Make Something Useful
So maybe you're not too impressed so far, but if you put a few in a row, add some nicer styles and realistic content, and you have an easy to maintain accordion panel.
Header
This is the body part, which will do the blind-up and blind-down effects
Run by a bunch of volunteers led by Paul Wheaton. He likes pie.
David O'Meara
Wrote this article.
The JavaRanch Volunteers
There are plenty of them, we even had to put a page together to track them all.
I did cheat a little here, if you look at the source code. Firstly the body parts start with the 'bodyClosed' rather than 'bodyOpen' class, and they have an initial style that makes them hidden. Also, the name of the header and body don't have to be numbers, they just need to be unique. It makes life easier if they are also meaningful, as long as you copy and paste them correctly.
Where Do We Go From Here?
If you thought that was too easy, that would be my point! Play with styles, play with storing the open/closed position in a cookie so it is retained between visits, look at rollover effects or different styles for open and closed headers. Keep in mind that now we are finished, the page content, CSS and script are about the same size, and the code knows nothing about the page content and page content just follows some easy class name and ID naming rules.
This article demonstrates two techniques that are frequently asked about: creating Java classes
at runtime, and evaluating a mathematical expression that is given as a string. While these two
don't have much to do with one another at first sight, the former can be used to implement a
well-performing solution to the latter. Performance matters in this case, because usually
formulas must be evaluated repeatedly for a range of parameters, at which point approaches
that are based on interpreters quickly become a limiting factor. On the other hand, approaches
that compile the formula to a Java class –like the one presented below–
will show much better performance.
We start out with creating a basic class that evaluates a formula given a single parameter.
Step by step the class is extended to be more developer-friendly and/or more powerful,
until at the end it handles multiple parameters while being easy to program with,
and easy to extend, at the same time.
The accompagnying zip file contains all example
codes and the Javassist library, which is used for dynamically creating classes.
Nothing else is needed to get the examples to run.
The Beginning
The following code shows the basic steps to create classes at runtime using the
Javassist library. Javassist is part of JBoss these days, but is otherwise independent of it;
it can be downloaded from SourceForge.
7) Object[] actualParams = new Object[] { new Double(17) };
double result = ((Double) meth.invoke(obj, actualParams)).doubleValue();
System.out.println(result);
Let's go through the steps one by one and look briefly at what they do. The following
examples are based on it, so it's important to understand the basic concepts now.
1) Javassists's ClassPool represents the set of all classes in a JVM.
It needs to be retrieved once, but nothing further is done with it except from adding the
new class to it.
2) This creates a new class named 'Eval'. CtClass is the most
important of Javassist's classes. It's the equivalent to java.lang.Class,
and is used to carry out just about all modifications one might want to apply
to a new or existing class.
If the newly created class should be part of a package, then its name must contain
the fully qualified class name, like it would be used in an import statement.
3) A method is created and added to the class. The body of the method is
as if it was part of a regular source file. The formula to be evaluated is passed in as an args parameter, and becomes part of the method. The method takes a double parameter and returns a double result.
4) Once we're done creating the class, we can retrieve its java.lang.Class object from the CtClass object. This concludes the use of Javassist - we can now use the new class like we would use any other class that was loaded dynamically.
5) We instantiate it, ...
6) ... use reflection to get a handle to the eval method, ...
7) ... and finally call the method with a parameter of 17 and print the result.
The expression to be evaluated is passed to the code as a parameter.
If you use this formula, the result should be 59 (= 17 + 42).
Case closed? Of course not. The reflection code is ugly, tedious to write, and
–if executed many times– not very performant. We want to write code like
result = obj.eval(17) instead. The problem with that is that we can't
use the class Eval in the code directly, because the class doesn't yet
exist when the code is compiled.
Making It Nice
Interfaces to the rescue! The class we create has only a single method we care about
(eval), and we know what its method signature will be. So we can move the
method into an interface, and have the to-be-created class implement this interface.
Then we can cast the object to the interface in step 5, and call the eval
method directly. This is the interface we'll implement:
public interface Evaluator {
public double eval (double x);
}
This is easy to do with Javassist. The following line declares that our class will
implement Evaluator:
evalClass.setInterfaces(
new CtClass[] { pool.makeClass("Evaluator") });
Then we can cast to it, and call eval:
Evaluator obj = (Evaluator) clazz.newInstance();
double result = obj.eval(17);
System.out.println(result);
This is what Example2 does. It is run in the same way as Example1,
except that it also takes the numeric argument from the command line, so that you can evaluate
the formula using other values besides 17.
One small additional change is that a timestamp is appended to the classname. This doesn't
change anything visibly –because we don't really care about the class name–
but it serves as a minimal guard against threading problems in case the class-constructing
code is ever run in a multi-threaded environment.
Adding Convenience
After using this code for a while you might want to evaluate more complex formulas, containing
constants and functions. No problem – the following lets us use "pi" instead of
having to type "3.141592..." in our formulas all the time:
evalClass.addField(
CtField.make("private double pi = Math.PI;", evalClass));
We can add random numbers by making use of the java.util.Random class. The following lines add a field for a Random object, a constructor that initializes it, and a rnd() method that returns a random number between 0 and 1.
Example3 shows this in action. Note that both adding a field and adding
a body to the default constructor works in much the same way as adding methods:
you supply the source code directly to the appropriate make method,
and add the result to evalClass.
Even More Convenience
Once you've added a few more functions and constants in the way described above, you're
probably wondering if there isn't a more convenient way to do this. Dealing with
Javassist's various classes, and having to maintain source code as part of a string
inside of other source code is not exactly fun.
Subclassing to the rescue! So far, the code has implemented the Evaluator
interface, which didn't allow us to supply any methods to go along with the eval
method. But nothing stops us from creating a class that extends some other class that has
implementations of all kinds of nifty functions. Look at the source code of the Expr
class – it does just that.
Example4 implements this. We just need to get a CtClass object
for the Expr class:
CtClass exprClass = pool.get("Expr");
And then make that the superclass of our expression-evaluating class:
evalClass.setSuperclass(exprClass);
That was easy. Now any additional functions can easily be maintained as part of the
Expr class. An alternative to implementing Evaluator in this
scenario would be to make eval an abstract method of Expr instead.
Whether one or the other makes more sense depends on how the class will be used;
functionally the approaches are equivalent.
Further Possibilities
Expression evaluation isn't limited to functions taking a single parameter, of course.
Example5 shows a program that takes either 1 or 2 parameters, and generates the
class accordingly.
These examples have only scratched the surface of what Javassist can do. In addition to creating
new classes, it's also possible to modify existing ones, and to remove fields or methods from a class.
The CtClass class also has methods for writing the bytecode of a class
to disk or obtaining it in a byte[], in case it's necessary to preserve it
beyond its one-time use.
Sometimes a class depends on other classes; in those cases there are ways to tell the
ClassPool about what to use as classpath.
There's also a low-level API for dealing with bytecode directly.
Conclusion
This article showed how to create Java classes dynamically, based on information available at runtime.
It then used those classes to evaluate mathematical expressions that were not known
beforehand. Both these are problems one doesn't encounter frequently, but they do occur,
and neither is trivial to solve. But knowing that it's not all that hard, additional
ways to make use of them probably suggest themselves.
Spring offers a few helper classes to do some scheduling in your application. In Spring 2.0, both the JDK's Timer objects and the OpenSymphony Quartz Scheduler are supported. Quartz is an open source job scheduling system that can be easily used with Spring.
Let's get started
As you may expect, both Spring and Quartz must be installed. Spring can be downloaded with all its dependencies. If you don't mind the size, it is recommended to download the bundle including dependencies. Quartz 1.5.x or above is needed in Spring 2.0.
It is assumed that you already know how to include jar files in your classpath. If you don't, there's no better place than JavaRanch to ask. It is also assumed that you already know Spring basics.
About Quartz
Quartz offers five main structures to realize scheduling:
The Job interface
The JobDetail class
The Trigger abstract class
The Scheduler interface
The SchedulerFactory interface
The Job interface represents a job. A job does something, only one thing. Its API is very straightforward. Only one execute() method, which will be called by Quartz when a Job has to be executed. Some useful information can be retrieved from the JobExecutionContext that is passed to the execute() method.
public interface Job {
void execute (JobExecutionContext ctx);
}
Some data can be passed to jobs via the JobDataMap class. If a JobDataMap is registered in a JobDetail, it can be accessed from the Job, via the JobExecutionContext which is passed to the execute() method of the Job interface.
The JobDetail class is used to give some information about a particular Job. Jobs are started (or "fired") by triggers, which are represented by the Trigger class. Quartz has some convenient implementation class of Trigger, such as SimpleTrigger and CronTrigger. A SimpleTrigger acts like a basic timer, where you can define a starting time, an ending time, a repeat count and a repeat interval. A CronTrigger uses more advanced settings, using the "cron" Unix utility notation. The settings for a CronTrigger can be very specific, like "fire the job at 10:15am on every last Friday of every month during the years 2008, 2009, 2010".
One notion is that jobs and triggers are given names, and may be assigned to a group. A name must be unique within the same group. So you can create a trigger for one group, and all jobs within that group will be executed.
Finally, the SchedulerFactory is used to get a Scheduler instance, which can be used to register jobs and triggers.
Let's illustrate this with a basic example : a simple job printing a welcome message, and a trigger firing the job every ten seconds.
First, the Job, only printing a welcome message:
public class RanchJob implements Job {
public void execute (JobExecutionContext ctx) throws JobExecutionException {
System.out.println("[JOB] Welcome at JavaRanch");
}
}
Then the scheduler, registering a trigger and a job :
public class RanchSchedule {
public static void main (String[] args) {
try {
SchedulerFactory factory = new org.quartz.impl.StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
scheduler.start();
JobDetail jobDetail = new JobDetail("ranchJob", null, RanchJob.class);
// Fires every 10 seconds
Trigger ranchTrigger = TriggerUtils.makeSecondlyTrigger(10);
ranchTrigger.setName("ranchTrigger");
scheduler.scheduleJob(jobDetail, ranchTrigger);
} catch (SchedulerException ex) {
ex.printStackTrace();
}
}
}
After executing RanchSchedule, [JOB] Welcome at JavaRanch is being printed out every 10 seconds.
There are some others interesting things like Priority, TriggerUtils, listeners, but they won't be discussed here.
If you want to know more about Quartz, there's a complete tutorial at Quartz's homepage.
Spring and Quartz
Spring's Quartz API resides in the org.springframework.scheduling.quartz package. There's quite a few classes there, but we'll only see those needed to fire up jobs.
The QuartzJobBean abstract class
The JobDetailBean class
The SimpleTriggerBean class
The CronTriggerBean class
The SchedulerFactoryBean class
The MethodInvokingJobDetailFactoryBean class
The wrapping of Quartz API is obvious. QuartzJobBean implements Job, JobDetailBean extends JobDetail. No need to say which class SimpleTriggerBean and CronTriggerBean extend. MethodInvokingJobDetailFactoryBean is a nifty class to call methods in any objects, instead of Quartz jobs. We'll see that last, but first let's make our previous RanchJob running. (Note that we won't use groups for our jobs and triggers)
Declaring Jobs
The JobDetailBean is used to declare jobs. We set the name of the job class, and if needed, some data the job may use. The job class extends QuartzJobBean. Here is the declaration :
Note how we retrieve the message from the context. This message was set in the bean declaration, using the jobDataAsMap property. Spring actually calls setter methods for each object set in this map. Here is the Spring way to use the message in the job class :
Declaring triggers is as simple as declaring a job. We'll only use a SimpleTriggerBean, which will start immediately, and repeat every ten seconds. You can refer to the CronTriggerBean API to check how to set a cronExpression for your trigger.
Everything has been set up, all we need now is to load the context. The scheduler will be started automatically on initialization.
I declared all the above beans in a file called "schedule.xml". I use ClassPathXmlApplicationContext here, so "schedule.xml" must be somewhere in the classpath.
public class RanchQuartz {
public static void main (String[] args) throws SchedulerException {
ApplicationContext ctx = new ClassPathXmlApplicationContext("schedule.xml");
}
}
Executing RanchQuartz will start the scheduler automatically. As expected, The RanchJob will be fired immediately, and repeated every 10 seconds.
Using the MethodInvokingJobDetailFactoryBean
We've seen so far how to fire Quartz jobs, but what if you want to call your own method of your own bean? With Spring, you are not tied to Quartz job instances, you can also use your own pojos to act as jobs. For example, you may wish to use the CronTrigger's advanced settings to fire up your own task.
Declaring the good old bean
Nothing special here. Just a good old bean, declared the good old way.
<bean name="welcomeBean" class="com.javaranch.journal.spring.quartz.RanchBean">
<property name="message" value="Welcome at JavaRanch" />
</bean>
And the implementation:
public class RanchBean {
private String message;
public void setMessage (String message) {
this.message = message;
}
public void welcome() {
System.out.println("[JOBBEAN] " + message);
}
}
Declaring the MethodInvokingJobDetailFactoryBean
When declaring the MethodInvokingJobDetailFactoryBean, we need to tell Spring which method of which bean to call. Let's also modify our trigger to use this new bean:
Voilà! Executing the RanchQuartz application again will produce the same result, with "[JOBBEAN]" being printed out instead of "[JOB]".
A word of caution
A trigger can fire only one job, but a job may be fired by many triggers. This can bring a concurrency issue. In Quartz, if you don't want the same job to be executed simultaneously, you can make it implement StatefulJob instead of Job. In Spring, if you are using the MethodInvokingJobDetailFactoryBean, you can set its "concurrent" property to false.
Finally
Using Quartz with Spring instead as a standalone application offers the following benefits :
Keeping all scheduling settings in one place. It makes scheduling easier to maintain. It looks a bit like having one "cron" file for scheduling tasks.
Coding only jobs. The scheduler, triggers and job details can be configured in the context file.
Using plain old java objects for jobs, instead of Quartz jobs.
As stated at the beginning of this article, Spring also supports JDK's Timer objects. If you want to use a simple timer for a simple task as above, you may consider using a TimerTask and Spring's ScheduledTimerTask and TimerFactoryBean instead.
If you decide to use Quartz in Spring, there are many more properties that can be set for the scheduler, jobs and triggers. Refer to Spring's API to discover them all.
"I got into the whole IT business through a backdoor..."
JavaRanch: First things first. How do you pronounce your name?
Lasse Koskela: "Lasse Koskela" -- how else can you pronounce it?
JR: Let's start with your first name. Which syllable do you usually stress, the first or the second?
LK: I usually stress the first syllable. Except when in France.
JR: And your last name?
LK: First. I like being first. Although I seldom are.
JR: How old are you?
LK: I'm as old as Brandon Lee when he filmed "The Crow".
JR: What about your parents?
LK: Yes, they are old. And I don't think they've seen "The Crow".
JR: [Aside: Um, OK. Let's see what you'll get for your "Yes, they are old."] You were born in Finland, right?
LK: Right.
JR: What city?
LK: Helsinki. I was actually the first in my family to born in Helsinki. My mother moved there in her twenties and liked it enough to hang around.
JR: When you were six year old, you wanted to be ...?
LK: My mother used to tell me I wanted to be an architect. A building architect, that is. But I think she just wanted me to become one ;)
JR: You seem quite attached to your mother. [Aside: there!] And you went to the college to study ...?
LK: Yes. I went to the Helsinki University of Technology for a few years but dropped out as soon as I had studied all the useful-sounding courses. Working in the bubbling IT business was too exciting to pass out on and eventually ran over the studies.
JR: Tell us about your working in the IT bubble.
LK: First of all, I got into the whole IT business through a backdoor. I had been working for a couple of media companies writing news articles about the IT industry when I was offered the opportunity to maintain and develop one of the group's many websites. I took the chance. The site was implemented with PHP and the code was utter crap, although I didn't really understand the magnitude of that crappiness back then. I lasted there for some months before I decided to move on. I ended up joining one of the cool new media companies and started working on their two web-based products. That was the best move I could've made.
One of the products was a rather complicated implementation of an online collaboration desktop targeted at advertising companies and the other was an online printing service. Together these two products gave me quite good a maze to run through and I learned loads about a variety of Java-related technologies, common design patterns for web applications, JavaScript (back then we called it DHTML, not Ajax ;), and even C++ because part of the system was implemented in C++. Oh, that C++ code base was also horrible, horrible code but, in hindsight, it was fun to try and figure out what's wrong with that in-house PDF generation software when everyone else is on vacation and I'm looking at the code for the first time.
Around those times I worked practically around the clock on a computer--either at work or at home. I sometimes jokingly say that I got two days of working experience every day... I just couldn't resist the thrill of learning new stuff. I went back and forth between programming stuff in Java to figuring out more artsy types of technologies like Flash. This lunacy resulted in me implementing my own PNG, PDF and XLS generating libraries--that was the "file format era"--and a personal website with more animation than should be allowed by the law. Good times.
JR: Can you explain what is "Test-driven development" in one sentence?
LK: TDD is a development technique that helps you create and evolve a design in a highly disciplined, safe manner in small steps of writing a test, making it pass, and cleaning it up. How's that?
JR: Yes, that's one sentence... Next question: you wrote "Test Driven ..." because you wanted to write it? Your publisher wanted you to write it? Somebody else wanted you to write it? Whose idea was it anyway?
LK: I didn't really want to write it but the voices told me I should. I was afraid they'd tell me to do something bad if I didn't obey. Oh, and the publisher might've had something to do with it, too. Seriously, it all started thanks to Mike Curwen who acted as a matchmaker between myself and the folks at Manning. "Test Driven" wasn't actually the first book idea that was discussed. For example, we also briefly considered a book on continuous integration. I turned that down, eventually, as it looked like I was putting together a software manual. TDD, on the other hand, was a perfect match considering how influential it had been for my own development.
JR: Could you give us a brief tutorial on "How to write a programming book"?
LK: The most important thing to keep in mind is to type fast. If you have any technology-specific material, you'll end up rewriting significant parts of those sections every 6 months so you want to finish before the next wave of open source project releases wets your manuscript.
JR: What was your publisher's role in the whole process? What helped you most?
LK: One part of Manning's role in the whole process was to make sure I write a book and not a series of random articles. That is, the wonderful editors they assigned to my project kept reading my drafts and pointed out structural issues with the table of contents and so forth. They made sure that I'll close the wide gaps between sections that would confuse the reader and generally ensured that there's a clear path through sections, chapters, and parts. It's hard to say what helped me most because taking away any piece of the puzzle would have such a drastic effect on the quality of the book. If I'd really have to pick just one thing as the most helpful, though, I'd probably say it was the development editors' advice on writing well on the small scale--writing sentences and paragraphs that flow well and are easy to read.
JR: In your interview with JavaLobby you said: "Manning's editors did an amazing job in schooling me about the various techniques and nuances that make for a good text." Can you share one tip?
LK: When I had signed the contract, Manning shared with me their secret authoring toolbox that contained all kinds of guides, documents, that described what kind of text they are looking for, what kind of techniques an author can and should use, and so forth. For example, one of these techniques is called a "crutch".
JR: A "crutch"?
LK: Yes. A "crutch" is essentially an introduction based on a concrete example that highlights a problem that is difficult to solve without the to-be-presented concept. It's a technique for helping the reader reach the right frame of reference, understand why she should continue reading the section.
This is the kind of stuff that I hadn't really thought about before. It's like I "knew" the technique and sometimes used it without thinking, but I wasn't aware of what I was doing. This kind of small techniques really help stitch together a seamless whole and understanding them makes it that much easier to become a good writer. Some people have it in them and some have to learn it. I belonged to the latter group.
JR: This photo...
LK: That's me. My first "exit", as they say. I've only done that four times. I was supposed to continue this summer but tennis came into the picture and skydiving lost the duel.
JR: How did it occur to you to jump off the plane?
LK: It all started with a friend of mine starting to skydive some three years back. He kept pestering me about giving it a shot, too, until last year I said "maybe I should try it some day". A week later, I get an email from the skydiving club saying, "thank you for registering for the course." Obviously I had done nothing and my friend knew I hadn't so he went and registered me on my behalf. And once I was up there, do you have any idea how embarrassing it would've been to not jump? It was fun, though.
JR: What did you feel during your first "exit"?
LK: Standing in the doorway, staring at the plane's engine, my only thought was, "why doesn't he give me the sign already?" That is, the jump instructor was supposed to slap me in the ass when I can jump. It felt like I stood there for 10 seconds before the slap. Half an hour later, looking at a video of my first exit, I realized it didn't take longer than one second... When I jumped, the only thing I was thinking of was getting that delta shape and arching my back as I was supposed to. Three seconds after the exit, I was already just hanging there with an open canopy. It was a wonderful feeling just flying back and forth high up in the air.
JR: Now the penultimate set of questions... Let's talk about your book.
LK: Why does the penultimate set have to be about my book? Aren't I much more exciting? I mean, at least I have a pulse and opinions.
JR: Of course you are! So we are not going to talk about your book?
LK: Isn't that exactly what we've been doing all the time? I just think the ratio between book-related and other stuff can be more balanced and less penultimate.
JR: OK. Let's not talk about your book. The first words in your book are:
To my colleagues,
for bugging me to finish this project.
And to my love Lotta,
who gave me the energy to do it.
JR: Lotta.
LK: Lotta is my girlfriend. She's a rollercoaster who makes my day with her smile. And roll my eyes with her unfathomable logic. She literally says the opposite of what she means, except when she doesn't.
JR: Can you think about any other question you want to answer?
LK: I'm already quite happy with the volume of stuff we have but maybe you could ask me something about how TDD relates to my work or to agile methods.
JR: How does TDD relate to agile methods?
LK: Well, the strongest link between TDD and agile methods is XP or Extreme Programming. This agile method, created by Kent Beck at Chrysler, had a set of 12 practices, of which TDD was one. People have worked test-first already decades ago but it's really XP that brought sexy back for TDD. Nowadays, however, I find TDD being more of a standalone practice in that I see all kinds of agile methods be applied in combination with XP engineering practices such as TDD. While TDD isn't an agile method, it's almost always applied by people using an agile method--although that doesn't have to be the case.
JR: What about your next book? Do you have any plans?
LK: I have plans. Several of them. Right now, however, I'm just enjoying the luxury of not having a manuscript in my lap crying for the much needed attention it's not getting.
JR: We are almost done. Lasse, thank you for your time. The only thing we need to do now is to add some human touches to all this dry and boring stuff. Could you tell us some jokes, please?
Not all the good stuff is in these Journal articles. The Saloon sees lots of insight and wisdom all the time,
and the various bloggers on the JavaRanch Radio are also busy turning out quality content. Plus, we've got
book authors hanging out with us all the time. So here are
a few nuggets you may have missed that should prove interesting to many folks.
Big Moose Saloon
In
Pair Programming Sucks?, ranchers discuss the attractions and drawbacks of pair programming,
as well as ways to maximize the former while minimizing the latter.
Everybody seems always on the lookout for the next web framework that has even more features than the last one.
A small antidote to this trend is the FrontMan framework, which takes a minimalistic approach to functionality,
and has recently seen increased attention and
discussion.
JavaRanch Radio
Balaji has examined a range of products for
Rendering XML, and presents the pros and cons of various approaches.
David has been looking for extra brains and muscle for his development team, and found an off-shore
solution in an unlikely location:
Off-shoring to the USA
Upcoming Promotions
On November 6, we've got Dmitry Jemerov answering questions about
IntelliJ IDEA 7
On November 13, Johanna Rothman introduces
Manage It!