Developing for Deployment
But often--whether through convenience or necessity--you need to develop and test your code on only one machine. It's quite easy to run into problems in such cases, simply because by running everything on one machine you're not exercising parts of your code that may cause things to break when the components of your distributed system run on different hosts.
For most Jini development, you will have three Jini-aware applications running: a lookup service, the service you're testing, and a client that uses that service (these applications are in addition to whatever other programs are needed, such as HTTP servers and RMI activation daemons). If all of these programs run on the same machine, and share the same resources (the same CLASSPATH, the same HTTP server, and so on), then potential problems with dynamic class loading or with security may be masked. Developing and testing in such an environment can allow problems to lurk unnoticed in your code.
This is the great danger to only testing locally. The apparent early convenience of taking the easy way out can result in greater headaches down the road. So even though a multimachine environment may require more up-front work, such as starting more HTTP servers, setting security policies, and so on, youll be rewarded for this effort by easier debugging and deployment later. For this reason, its a good idea to get in the habit of thinking about multimachine environments, and even simulating such environments when you develop and test.
Fortunately this simulation is fairly easy to do. There are some simple tips you can follow when you run and test your code to ensure that it will work in such a multi-machine environment, even if youre developing on only one computer. These tips address most of the common problems in a multimachine setting.
These are the guidelines I follow when developing Jini applications, and they're the guidelines that I follow in the Core Jini book. I hope they help. Here are the tips I recommend following when developing.
Run Multiple HTTP Servers
If you can clearly separate all the downloadable code for a given program on one HTTP server, and then run that program with a codebase property that instructs clients of the program to fetch code only from that HTTP server, then you can tell when you don't have all the necessary code together in one place--the clients will complain that they cannot load certain needed classes.
There are a few good general tips you should follow when setting the
codebase property, though. First, never use file:
URLs. If
a server passes a file:
URL to a client, the client will
try to download any needed code from its own local filesystem. If
you're developing and testing both your client and your server on the
same machine, your code may work--since the class files will live in
the same place for both client and server. But if you ever run the
client and server on separate machines that do not share a filesystem,
your code will suddenly break.
Likewise, never use "localhost" in a codebase as a hostname.
Localhost
is used to refer to the current host, so if a
server sets the codebase to a URL containing localhost
,
the client will evaluate this codebase and attempt to load the code
from its system, rather than the server's. Again, this
situation may deceptively work if you're testing the client and
the server on the same machine, as you're likely to do. But if you use
actual hostnames in codebase URLs, and run separate HTTP servers for
each, as noted above, then you can identify potential problems in code
loading early.
System.setSecurityManager()
. The
security manager ensures that any classes that are loaded
remotely--through a codebase that is provided via RMI--do not perform
any operations they are not allowed to perform.If you do not set a security manager, then no classes will be loaded other than those found in the application's CLASSPATH. So, if you only test locally, and do not set a security manager, your code may still work because the classes that would otherwise have to be downloaded may be found in your CLASSPATH. But your application will definitely fail in a multimachine setting.
You can, for testing purposes, use a very "promiscuous" security policy that allows free and unfettered access to any resources. While such policy files are fine for testing "known" code--meaning code that you have written--it's definitely not suitable for a production environment.
Here's a simple example of a security policy file that grants all permissions:
grant { permission java.security.AllPermission; };
file:
URLs or shared HTTP servers. One other way that code
can be shared between two applications--and this is the way that most
of us are familiar with--is by running the applications on the same
machine and with the same CLASSPATH. If both a client and a server are
sharing class files off the disk, then it's virtually impossible to tell
what specific classes these programs will need to access remotely. So
to prevent unanticipated sharing of class files, you may consider
running without any CLASSPATH at all. Instead, try passing the
-cp
option to the java
bytecode interpreter.
This option lets you specify a series of directories and JAR files to
load class files from. Even if you're developing your client and server
on the same machine, you can keep the class files for each in separate
directories, unset your CLASSPATH, and use a different -cp
argument to the java
interpreter for each to ensure that
no unwanted crosstalk exists between your applications.
Furthermore, it's a good habit to get into to provide only those
classes the application needs to do its job, rather than all class
files "just to be sure." You can start off with the three Jini JAR
files in the -cp
argument (jini-core.jar
,
jini-ext.jar
, and sun-util.jar
), and then
copy in your own specific application class files as needed. The
compiler is a great help in identifying which class files you need to
install, as it will complain if it cannot find needed classes.
Under no circumstances should you put the -dl
JAR files
(reggie-dl.jar
and so on) from the Jini distribution into
your directory of classes--these are meant to be dynamically downloaded
from the core Jini services. If you put these in with your application
classes, you'll simply be ensuring that you're using the versions of
these files that you got with Jini, and not the version that the lookup
service expects and tells you to use.
reggie.jar
, and the classes that
will be downloaded to clients in reggie-dl.jar
. The HTTP
server that exports the lookup service's downloadable code has its root
directory set to a directory containing reggie-dl.jar
. The
codebase property for the lookup service provides a URL naming the HTTP
server that specifies where clients should download the classes in
reggie-dl.jar
.You should consider breaking your classes into chunks for the implementation and the downloadable components. Creating separate JAR files for each is also handy when you need to move your service, or change where clients download your service's code.
Setting up your development environment in this way is a bit of trouble; but in my experience it can pay off in a big way as you begin to develop more complex services and applications.
Keith Edwards
kedwards@kedwards.com