In June 2006 Sun announced that it would be releasing the Java source code
under the Gnu Public Licence. Java has always been freely available to
download but it has come under Suns own licence. This meant it could not be
freely bundled with Linux distributions and it added a slight overhead to
any ideas of simply giving it away. There had been considerable debate as to
what licence Sun might use and the choice of GPL caused some controversy.
The key to the GPL is that anyone can give away or sell the software but the
person they give away or sell it to receives the same rights, i.e. they too
can sell or give the software away. Of course, actually selling free
software is quite a difficult task, but the GPL does not actually
forbid the commercial sale of software.
The GPL announcement was as much about a change in mindset as about the
practicalities of obtaining Java. It means that in the future Java will
almost certainly be bundled with every complete Linux distribution and it
will be possible to give it away just about as you please. This new licence
adds to the reality of portability of Java. In the past, although Java was
available for free download it required a little expertise and understanding
for it to be usable under Linux. I have wasted several hours of my life
attempting to get Java configured to use Applets with Netscape and then
Firefox under various Linux distributions and I'm a relatively expert Linux
user. Previously, when a programmer was choosing a language for Linux
development they might choose Perl or C/C++ because they could be safe in
the knowledge it would be "available out of the box", in the future they
will be able to make that assumption about Java.
Although the GPL announcement promises great things in the future the
current and near term reality is a little different. Even though the
announcement was over a year ago there is still no binary version of GPL
Java for you to download. The source that you can download is effectively
the same as Suns JDK 7 and the feature set for that is not even close to
having been finalised. According to
one estimate,
Java 7 and thus the GPL version of Java might be available at the start of
2009. I suspect that the new features of Java 7 will be fairly minimal as
Sun and the other developers will be concentrating on the work
necessary for the GPL code.
On reading that you might be asking yourself why, if Sun has the source code
they don't just compile it up right now and make the GPL version available
as a binary download. Part of the problem is that certain parts of Java come
from third parties and cannot be released under the GPL by Sun. As a result
a significant amount of work has to be done to re-write these bits of code
and to test and integrate them into the main Java distribution. I downloaded
the openjdk source code and building it is not for the faint hearted. Under
windows it appears to require Microsoft Visual Studio .NET 2003 Professional
plus the Cygwin utils to create a Unix like environment. In the meantime you
can download "snapshot" builds of JDK from
http://download.java.net/jdk7/binaries/.
It's probably an indication of just how early these builds are in that the
readme.html file that ships with the snapshot is actually the same file as in JDK 6.
Despite the delay between announcement and availability of GPL binaries the
use of the GPL licence changes the playing field, it sets down a market that
says "Java will be everywhere". If that sounds a little unlikely it is
worthwhile looking at the broader picture of the world of Java. It's
tempting to think that Java is an important programming tool, but just one
of many. If you treat Java as the platform itself, in the same way as people
look at operating systems, Sun can spin the story in a very interesting way.
Java Everywhere
According to Jonathan Schwartz of Sun
"Few folks, at least outside of Sun, understand how pervasively successful
the Java platform, and the community supporting it, have been over the past
decade. But Java runs on more devices than Microsoft Windows, Linux,
Solaris, Symbian and the Mac combined. Nearly 4 billion devices at this
point, from smart cards to consumer devices, DVD players to set top boxes,
medical equipment, all the way up into the majority of the world's
transactional systems and 8 out of every 10 cellphones sold. The Java
platform is, already, a global standard."
Read that again, "more devices than Microsoft Windows, Linux, Solaris,
Symbian and the Mac combined". Even allowing for industry hype and plenty of
those machines being very low spec systems you are unlikely to program for
it is still a very impressive piece of market share. Think of that if anyone
tells you that C# or python is the future. The GPL announcement also makes
some of the other Java players feel a little more relaxed. It is widely
rumoured that IBM has more programmers working on Java than Sun, but they
have always been a little nervous that Sun seemed to have control over its
direction and development. Under the new GPL licence Sun will still have
considerable control over Java because they will control the ability to use
the name Java. Thus if you "fork the code", i.e. create a new version based
on the GPL code that differs from Suns version you will be able to give it
away, sell it and use it as you can any other GPL code, but you won't be
able to call it Java. You will have a tough time getting people to adopt
your forked version as people are used to the comfort and security of the
Sun version for the last 10 years. However if push comes to shove a big
organisation like IBM has a greater comfort zone with the GPL and less
reliance on the goodwill of Sun.
Java may no longer be the shiny new technology but it is the entrenched
standard, and is likely to remain so for a long time.
And when I say "Enterprise" I do mean a collection of servers and application components, although the "U.S.S. Enterprise" does have a cameo!
Most server based applications installed today have no system monitoring. The way to tell if the system is down is when the phone calls start coming. Sometimes at home. This sort of thing can really put a damper on the pay raise department.
And this topic gets richer - when you have to do an emergency upgrade, how do you know something you changed didn't screw up the production system in a whole different way? Phone calls again? More pay raise dampening.
A collection of simple system monitors and diagnostics can help you to be sure that your application is installed correctly
and currently functioning correctly. And when there is a problem, not only can you be notified immediately, you can
view the information to find out where, exactly, the problem is. If the problem is fixed quickly, or fixed before anybody notices, this bodes well for the pay raise stuff.
Nagios is a popular tool for doing a variety of system monitoring, but in my experience, Nagios is used almost exclusively
for monitoring hardware. So if your application can no longer talk to the database, nagios thinks everything is just fine.
The solution is to add some simple stuff to your app that you and nagios can use to make sure everything is okay.
A simple servlet is a great way to go for this sort of thing. A diagnotic can return something like "OK" or "FAIL"
so any web browser can run a diagnostic on a component. Plus, nagios (and similar products) are able to exercise
web pages too.
In "Star Trek: The Next Generation", Picard will tell somebody to run a "Level 3 diagnostic"
for some part of the Enterprise. It turns out that the writers of this show have worked out what, exactly, that means.
The following is an attempt to use their terminology to fit the needs of server development. Please note that level
1 and level 2 diagnostics require shutting down the application. I think that this sort of thing is better represented
by testing, so I've left those out.
Level 5 Diagnostic
Select a few things to do that can complete in two seconds or less.
Configure nagios to exercise this diagnostic about once every two minutes.
The first line of text returned must be "OK" or "FAIL" for nagios.
Additional human readable text would be nice.
Some things that might be exercised:
read from the database (verify that communication with the database is funtioning)
ability to put a NO-OP message into a JMS queue (verify that JMS is functioning)
make sure all JMS queue sizes meet expected parameters
verify that app can read and write to the file system (testing for permission problems and file handle problems)
number of objects in memory meets a threshold
number of objects in the file system meets a threshold
certain files (logs?) are not getting too large
certain recent activities are within "normal operating parameters"
HTTP server is serving a tiny web page
recently logged error or warning
is data current?
Level 4 Diagnostic
select a rich list of things to do that can complete in 30 seconds or less.
Configure nagios to exercise this diagnostic about once every two hours.
The first line of text returned must be "OK" or "FAIL" for nagios.
Additional human readable text would be nice.
Some things that might be exercised:
exercise the Level 5 Diagnostic for this component
check something in the Level 5 diagnostic list above that could not fit into 2 seconds
ping components the app depends on (verify that the network is functioning between the two components)
exercise all JMS queues
FTP read/write
exercise EJB interface
exercise MDB interface
exercise SOAP interface
exercise RMI interface
examine JDBC driver version
exercise a sophisticated algorithm
Level 3 Diagnostic
select a rich list of things to do that can complete in 5 minutes or less.
Configure nagios to exercise this diagnostic about once every day.
The first line of text returned must be "OK" or "FAIL" for nagios.
Additional human readable text would be nice.
Some things that might be exercised:
exercise the Level 4 Diagnostic for this component
check for when licenses expire for third party products
exercise a workflow.
Needs of the real world are a bit more than in star trek. A few more diagnostics for the repertoire:
Ping
Configure nagios to exercise this diagnostic about once every 30 seconds.
Just return "OK".
This shows that the network and most systems are functioning and that the component is not locked up.
Installation Diagnostic
Called by the person installing the component (not by nagios) immediately after installation.
Recycle stuff from the level 3, 4 and 5 diagnostics.
Check to make sure jar file code can be exercised.
Called as needed by developers and system operators (not by nagios).
Status page
Called as needed by developers and system operators (not by nagios).
This might show:
a detailed list of which systems are functioning correctly and which are not so good
log data
the version number for the jdbc driver, the app server and the java version
the current sizes of JMS queues
the number of servlet requests
If you implement this servlet only with ping, you have made a big leap. Calling this ping, even manually, will verify that
your component is not locked up and that the network is functioning at least in part. If you later connect nagios to
the component ping, you can probably find out about a problem with your component long before you get that first call
from a user of the component.
Overall, implementing all aspects of this servlet would probably take less than a day. Connecting it to nagios would
take about an hour. And then ... if there ever is a problem,
you should be able to resolve the problem about 20 times (maybe 100 times) faster than without it.
Last year I wrote an
article on Web Services authentication.
At the time Axis 1.x was used for the examples, but by now Axis 2 has been released,
and I want to talk about the changes that this new version brings about. The
first article is referenced repeatedly, so you may want to skim it at least
before proceeding with this one.
An important improvement in
Axis 2 is that WS-Security is now more tightly integrated with it. It used to
be that the WSS4J library (which implements WS-Security) was a separate
project, and needed to be added manually to an Axis installation, but no more.
An Axis module called Rampart wraps the WSS4J functionality, and
can be added to a base Axis installation with little effort.
Because WS-Security is so
readily available now, it should be the preferred method of authentication.
HTTP authentication as described in the first article can still be used (since
Axis is implemented as a servlet in a web app), but it should be considered
obsolete and be phased out. I will not discuss it here. Nor will I address the
Tomcat Realm integration, because nothing has changed in that regard.
I will present two kinds of WS
clients, one using the SAAJ API which was also discussed in the first article,
the other one using Axis own API. Axis 2 no longer supports JAX-RPC, so that
client is missing here (JAX-RPC is being replaced by JAX-WS, but that's not yet supported by Axis).
Preparation
A couple of steps need to taken before we can experiment with authentication.
The example files discussed in this article can be downloaded here:
axis2-auth.zip
Axis 2 needs to be installed and 'happy'; this can be checked on
the Axis installation home page (http://localhost:8080/axis2/) under the
Validate link. Also, the AXIS2_HOME environment variable needs to set to the
directory where the Axis distribution is installed.
Next, the WS-Security module Rampart needs to be
installed. For this, copy the rampart-1.1.mar and addressing-1.1.mar files that are part
of the example download to the
WEB-INF/modules directory and restart the Axis web app. Now Rampart should be
listed on the Axis Admin page (http://localhost:8080/axis2/axis2-admin/, default
username is 'admin' with password 'axis2') under Available Modules.
Finally, the example web service must be installed. Copy the FibonacciService.aar file into the
WEB-INF/services directory and once more restart Axis. FibonaciService should
now be listed under Available Services, and it should report that rampart-1.1
is an engaged module for it. (Alternatively, the service archive file can also
be uploaded through the Upload Service link on the Admin web page.)
Example
Now we're ready to play. Open a command prompt in the directory containing the examples,
and start the tcpmon application by typing ant tcpmon.
This will show the SOAP requests and responses in a convenient way.
Then run the SAAJ client by typing ant test-saaj -Dn=15 -Dport=8079.
The 'n' parameter is the input to the
Fibonacci function. You can choose a different one, but then the result will
not be 610. The port 8079 is the one used by the tcpmon tool; use 8080 if you
want to go straight to the Axis web app, thus not monitoring the call. (Note
that the SAAJ client also logs the request and response to the console; I've
not found a way to turn that off.)
All the interesting WS-Security information is in the wsse:Security
element that's part of the SOAP header. It can have several child elements;
in this case, wsu:Timestamp and wsse:UsernameToken are present.
Why these, and what do they mean? WS-Security can perform 4 different actions:
Timestamping, Authentication, Encryption and Signature. Here, the first two are
used. A timestamp is used to indicate how long the supplied credentials (here:
the username/password) are valid, and the service is supposed to reject them if
it thinks that they are too old. Since we're focusing on the authentication,
I'll not address timestamps in any more detail. They're optional in any case.
The username token essentially
consists of the username and the password, which is digested in this case
(meaning it can't be recovered should the message be intercepted).
The actual body is rather
short, carrying the calculateFibonacci call with parameter 15. The response
carries no WS-security information, just the calculateFibonacciResponse with result 610.
It specifies that a UsernameToken and a Timestamp should be
generated (the Timestamp being
optional, as stated above). Furthermore, it tells Axis which username to use,
and which class will supply the password that goes along with this username.
Other options (commented out here) include the use of a cleartext password
(instead of the digested one that is created by default), and how to specify a
longer time-to-live for the timestamp (10 seconds instead of the default 5).
The remainder of the file contains the non-WS-Security setup of Axis.
In addition to the conf/axis.xml file, both
the Addressing and Rampart Axis module need to be available on
the client side. The two APIs differ slightly in how to tell the application
where to find these. For the SAAJ client, two system properties (axis2.repo
and axis2.xml) must be set, while for the
Axis API, these can be set programmatically (and thus are passed in via command
line parameters). The build.xml file shows how to do that for both.
The server side config file (resources/META-INF/services.xml) has a
similar section that specifies which WS-Security actions to perform, and which
class to call to check the password:
The Axis API client produces
the same request and response; it is run via ant test-axis2 -Dn=15 -Dport=8079.
Files in the examples download
axis2.log - log file written by the Axis client classes
build
classes - where the compiled Java classes are kept
FibonacciService.aar - service archive for deployment, created by ant generate.service
FibonacciService.wsdl - WSDL description of the service, created by ant generate.wsdl
build.xml - Ant build file
conf
axis2.xml - client side deployment descriptor
log4j.properties - log4j config file
modules
addressing-1.1.mar - Axis module that implements WS-Addressing
rampart-1.1.mar - Axis module that implements WS-Security
(These versions are appropriate for Axis2 1.1; if you're using a different version,
you should download its associated module files from the Axis2 web site.)
resources
META-INF
services.xml - server side deployment descriptor, goes into FibonacciService.aar
src
fibonacci
Axis2Client.java - WS client using the Axis API
FibonacciService.java - the class implementing the service
PWHandlerClient.java - handles passwords on the client
PWHandlerServer.java - handles passwords on the server
SAAJClient.java - WS client using the SAAJ API. It has a commented-out
section that shows how to set and use arbitrary SOAP headers.
tcpmon.jar - standalone Swing app that can act as a proxy for web service
requests and responses, and shows those in a structured way
In recent years, Dependency Injection may have buzzed into your ears quite often. You may already know that it has nothing to do with drug addiction, but with some cool object-oriented concept.
You may also have heard of the Spring Framework, a so-called Dependency Injection container. You might be under the impression that Dependency Injection equals Spring. But no, Dependency Injection is a simple concept which can be used anywhere, without any Dependency Injection container, and especially useful in unit testing.
In this article, we will see
what Dependency Injection is
how to make a class Dependency Injection friendly
why Dependency Injection can ease unit tests
Ladies and gentlemen, start your engine !
A simple car
Let's consider a simple example, using engines and cars. We'll leave classes and interfaces
empty for clarity. A car has an engine, and we'd like that car to be equipped with JavaRanch's
famous MooseEngine™.
We've got engines:
public interface Engine {
}
public class SlowEngine implements Engine {
}
public class FastEngine implements Engine {
}
public class MooseEngine implements Engine {
}
And we've got a car:
public class Car {
private MooseEngine engine;
}
This is a great car, but it cannot have any other kind of engine, although there are other brands
available on the market. We say that the Car class is tightly coupled to the MooseEngine class.
There's nothing wrong with it, the MooseEngine™ is great, but what happens if we finally decide
to equip it with another engine ?
Programming with interfaces
You may have noticed that the MooseEngine™ was implementing the Engine interface.
Other brands are also implementing the same interface. Let's think about it. When we designed
our Car class, we thought that a "car" was equipped with an "engine". So let's rewrite the Car
class to use the Engine interface instead:
public class Car {
private Engine engine;
}
Programming with interfaces is an important concept in Dependency Injection. I can already
hear you screaming, "wait a minute! That's an interface you're using here, where is the
concrete class? Where do you set it? I want my MooseEngine™ in my car".
We could set it the following way:
public class Car {
private Engine engine = new MooseEngine();
}
But would that be useful ? It doesn't look much different from the first example.
Our car is still tightly coupled to our beloved MooseEngine™. So?
Where do we set (or inject) our car's engine ?
Introducing Dependency Injection
As its name says, Dependency Injection is all about injecting dependencies, or simply said,
setting relations between instances. Some people refer to it as being the Hollywood
principle "Don't call me, we'll call you". I prefer calling it the "bugger" principle:
"I don't care who you are, just do what I ask".
In our first example, a Car depended on an Engine's concrete class called MooseEngine.
When an class A depends on another class B, and that B's implementation is set directly in A,
we say that A is tightly coupled to B. As we've seen in the second example,
we have decided to use the Engine interface instead of the MooseEngine™ concrete
class to make the Car more flexible. Moreover, we decided not to define the engine's
concrete implementation. In other words, we have made our Car class loosely coupled.
The Car doesn't depend on any concrete class of Engine anymore. Where do we indicate which
Engine to use then ? That's where Dependency Injection comes. Instead of setting the concrete
Engine class in the Car class, the concrete Engine class is injected from the outside. How do we do that?
1. Using constructor based injection
One way to set dependencies is to pass the concrete implementation of the depending
class to the constructor. Our Car class would become:
public class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
}
We could then create a Car using any kind of engine. For example, one car using the great
MooseEngine™ and another one using the crappy SlowEngine:
public class Test {
public static void main(String[] args) {
Car myGreatCar = new Car(new MooseEngine());
Car hisCrappyCar = new Car(new SlowEngine());
}
}
2. Using setter based injection
Another common way to set dependencies is to use setter methods. Using setter methods is
recommended instead of the constructor when many dependencies need to be injected. Our car
class would then be written that way:
public class Car {
private Engine engine;
public void setEngine(Engine engine) {
this.engine = engine;
}
}
It looks very similar to the constructor based injection, doesn't it? We would then implement
the same cars we have used above the following way:
public class Test {
public static void main(String[] args) {
Car myGreatCar = new Car();
myGreatCar.setEngine(new MooseEngine());
Car hisCrappyCar = new Car();
hisCrappyCar.setEngine(new SlowEngine());
}
}
Using Dependency Injection for Unit Testing
If you compare the first example of the Car class, and the one using setter based injection,
you might think that some extra steps were needed to make the Car class using Dependency Injection.
That's right, you had to write a setter method. But you can feel how rewarding this extra work is
when unit testing.
If you are not confident about what unit tests are, I recommend this
Campfire Story on Evil Unit Tests.
Our Car example is so simple that it does not illustrate well how useful Dependency Injection
is for unit testing. We'll leave cars, and consider the example provided in the forementioned
Campfire Story, especially the part on
using mocks for unit testing. We have a servlet class using a remote EJB to register animals in a farm:
public class FarmServlet extends ActionServlet {
public void doAction( ServletData servletData ) throws Exception {
String species = servletData.getParameter("species");
String buildingID = servletData.getParameter("buildingID");
if ( Str.usable( species ) && Str.usable( buildingID ) ) {
FarmEJBRemote remote = FarmEJBUtil.getHome().create();
remote.addAnimal( species , buildingID );
}
}
}
You have already noticed that FarmServlet is tightly coupled to the FarmEJBRemote instance,
which was retrieved via a call to "FarmEJBUtil.getHome().create()". It makes it very hard to test.
When unit testing, we don't want to use any database. We don't want to access an EJB server either.
That would make unit tests both difficult to execute and slow. So in order to unit test the
FarmServlet class smoothly, we'd better make it loosely coupled. To remove the tight
dependency between FarmServlet and FarmEJBRemote, we could use a setter based injection:
public class FarmServlet extends ActionServlet {
private FarmEJBRemote remote;
public void setRemote(FarmEJBRemote remote) {
this.remote = remote;
}
public void doAction( ServletData servletData ) throws Exception {
String species = servletData.getParameter("species");
String buildingID = servletData.getParameter("buildingID");
if ( Str.usable( species ) && Str.usable( buildingID ) ) {
remote.addAnimal( species , buildingID );
}
}
}
In the real deployment package, we will make sure that an instance of FarmServlet's remote member
will be properly injected via "FarmEJBUtil.getHome().create()". In our unit test, we will use a
dummy mock class to act like a FarmEJBRemote. In other words, we will make a mock class implementing
FarmEJBRemote:
class MockFarmEJBRemote implements FarmEJBRemote {
private String species = null;
private String buildingID = null;
private int nbCalls = 0;
public void addAnimal( String species , String buildingID )
{
this.species = species ;
this.buildingID = buildingID ;
this.nbCalls++;
}
public String getSpecies() {
return species;
}
public String getBuildingID() {
return buildingID;
}
public int getNbCalls() {
return nbCalls;
}
}
public class TestFarmServlet extends TestCase {
public void testAddAnimal() throws Exception {
// Our mock acting like a FarmEJBRemote
MockFarmEJBRemote mockRemote = new MockFarmEJBRemote();
// Our servlet. We set our mock to its remote dependency
FarmServlet servlet = new FarmServlet();
servlet.setRemote(mockRemote);
// just another mock acting like a ServletData
MockServletData mockServletData = new MockServletData();
mockServletData.getParameter_returns.put("species","dog");
mockServletData.getParameter_returns.put("buildingID","27");
servlet.doAction( mockServletData );
assertEquals( 1 , mockRemote.getNbCalls() );
assertEquals( "dog" , mockRemote.getSpecies() );
assertEquals( 27 , mockRemote.getBuildingID() );
}
}
That's it! We've got an easy to test FarmServlet.
To sum up some important points:
Use interfaces instead of using concrete classes to illustrate a dependency.
Avoid to implicitly set the concrete implementation of a dependency in the class itself.
Concrete implementation of a dependency may be set in various ways, including constructor
based injection or setter based injection.
Dependency injection makes unit testing very flexible.
It's been a while since the last Journal, so instead of a Book Review of the Month,
we present those reviews that have scored a whopping 10 out of 10 horseshoes
during the last year.
Mastering Regular Expressions by Jeffrey E. F. Friedl
"Mastering Regular Expressions" takes a great book and modernizes it to include the latest
programming languages. The book starts out with assuming you know anything about Regular Expressions
-- aside from the concept to be interested in picking up the book.
The author introduces regular expressions through examples and quickly introduces the constructs.
The second third of the book goes into the details of how regular expressions are processed.
This includes correctness and efficiency issues. The final third on the book goes over the syntax
in Java, .NET, Perl and PHP. Tools like grep and awk are described in the text as well.
An alternate title for this book would have been "Thinking in Regular Expressions."
Even if you think you know regular expressions, this book teaches you how much more there
is to learn. It also teaches you some of the finer points of regular expressions in your
favorite programming language along with cross references to the earlier part of the book.
The author uses good analogies to make the text understandable. After awhile, the concepts
get so complicated that you have to read it many times to understand. A typically O'Reilly book.
I've only had this book two weeks and I've already used it to make me a more effective developer!
Many of the texts on software engineering discuss following some methodology to produce an ideal design.
Working developers quickly learn that the ideal is rarely reality and things happen once we release
software out into the wild. Michael Nygard's "Release It!" picks up where these other books leave off.
Nygard talks about all the things that can and will go wrong in the finely crafted software we were
sure was ready for production. A full two-thirds of the book is focused on capacity and stability
issues including patterns and anti-patterns for both. The remainder of the book deals with general
design issues as well as maintaining health and status in an operational system. "Release It!"
provides many first hand accounts to illustrate his points, beginning with the Exception that
grounded an airline, and these stories serve as excellent motivators. It's better to learn from
the mistakes of others, and I really appreciated the detail Nygard went into addressing some of
these horror stories.
The Pragmatic Programmers have a few "must read" books and "Release It!" is another one.
After reading it and heeding its advice, you'll feel a bit better knowing that your software
is better prepared for the rigors of production.
Continuous Integration by Paul Duvall, Steve Matyas and Andrew Glover
"Continuous Integration" is part of the Addison Wesley series. This series includes books like
"Refactoring to Patterns". "Continuous Integration" definitely meets the standards of this series.
Each chapter describes CI related practices. There is a chapter dedicated to risks reduced by CI
including anti-patterns like "It works on my machine." Each chapter ends with questions to get
you thinking about CI in YOUR process. I particularly like how the authors address the "CI is
good but my project is special" problem.
The authors give examples in different languages including Java, .NET and Ruby. The appendices
on resources and tools are very useful. The book goes beyond CI and addresses continuous inspection
and deployment. My only problem when reading the book is that I forgot I was supposed to be
writing a review. It was so good, I just got caught up in the book!
Do check out the companion website integratebutton.com.
It currently contains video examples of three practices described in the book.
The materials are presented in slide and diagram format.
It reinforced the book nicely because it was like a guru explaining his experiences.
It also goes into much more detail than the book has room for on each topic.
This is an excellent book and the website adds to it!
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
How do you interview?, ranchers discuss the perennially hot question of what you should ask
in a techical interview to get an idea of how good a candidate really is, or whether he's faking it.
Lasse talks about
Testing for expected exceptions with JUnit. He promises that it's not just a plug for
his book, but is actually a subject that has seen some changes in JUnit 4, so it has definite timeliness.
Gregg admits that he pieced together bits and pieces from all over the place in
Session Timeout and AJAX,
but since nobody seems to have put it all together before in one place, this is your chance to
learn how to deal with it.
Finally, David O'Meara creates an image containing rotated text in a servlet on the fly, and
streams it to the browser in
Vertical text from a Servlet.
Being from the upside-down country of Australia, we can forgive David for not reading left-to-right
like most of us.
Upcoming Book Promotions
On September 18, we've got Chet Haase & Guy Romain talking about
Filthy Rich Clients
On September 26, JavaRanch's Lasse Koskela introduces
Test Driven
On October 2, the trio of Vivek Chopra, Sing Li & Jeff Genender dispense the Tomcat
knowledge of Professional Apache Tomcat 6